Olej писал(а): ↑08 фев 2024, 13:23
нужно разбираться с context
Разбираемся с контекстами в Go
Ильдар Карымов
December 5, 2021 · 9 min
Я заметил, что тема контекстов в языке Go у многих почему-то вызывает сложности с пониманием. Возможно, это связано с тем, что контекст — это очень абстрактная сущность и не встречается в других языках программирования в таком виде, по крайней мере в тех языках, что довелось использовать мне.
Если мы взглянем на документацию к пакету context, то первый абзац будет таким:
Пакет context определяет тип Context, который позволяет управлять дедлайнами, сигналами отмены и другими значениями области действия запросов между границами API и процессами.
Что это, чёрт побери, значит?
А значит это примерно следующее:
Контекст — это объект, который предназначен в первую очередь для того, чтобы иметь возможность отменить извне выполнение потенциально долгой операции. Кроме того, с помощью контекста можно хранить и передавать информацию между функциями и методами внутри вашей программы.
Отменять долгие операции с помощью контекста можно несколькими способами:
- По явному сигналу отмены (context.WithCancel)
- По истечению промежутка времени (context.WithTimeout)
- По наступлению временной отметки или дедлайна (context.WithDeadline)
Вы можете спросить: а что за context.Background()?
Дело в том, что любой контекст должен наследоваться от какого-то другого, родительского контекста. Исключения: Background и TODO. Background — это контекст-заглушка, используемый как правило как самый верхний родитель для всех дочерних контекстов в иерархии. TODO — это тоже заглушка, но используется в тех случаях, когда мы ещё не определились, какой тип контекста мы хотим использовать. Эти два типа контекста по сути одно и тоже, и разница исключительно семантическая.
Окей, зачем нужна схема с родительскими и дочерними контекстами? Это сделано для того, чтобы внутри функции, куда был проброшен контекст, не было возможности повлиять на условия отмены сверху. Таким образом мы имеем гарантию (с некоторым оговорками), что контекст с дедлайном отменится не позже данного дедлайна.
Мудрёно
Ну ... несколько да
Окей, а что насчёт передачи значений через контекст? Для этого в пакете существует функция WithValue.
...
Когда стоит передавать данные через контекст?
Короткий ответ — никогда. Передача данных через контекст является антипаттерном, поскольку это порождает неявный контракт между компонентами вашего приложения, к тому же ещё и ненадёжный. Исключение составляют случаи, ...
Круто?
А то
Советы и лучшие практики
- Передавайте контекст всегда первым аргументом — это общепринятое соглашение;
- Передавайте контекст только в функции и методы, не храните в состоянии (внутри структуры). Контексты спроектированы так, чтобы их использовали как одноразовые и неизменяемые объекты. Например, если вы сохраните контекст с таймутом в 15 секунд в поле структуры, а спустя 15 секунд попробуете выполнить операцию с данным контекстом, у вас ничего не получится. Обнулить счётчик таймаута вы тоже не сможете;
- Используйте context.WithValue только в крайних случаях. В 99,(9)% случаев вы сможете передать данные через аргументы функции;
- context.Background должен использоваться только как самый верхний родительский контекст, поскольку он является заглушкой и не предоставляет средств контроля;
- Используйте context.TODO, если пока не уверены, какой контекст нужно использовать;
- Не забывайте вызывать функцию отмены контекста, т.к. функции, принимающей контекст может потребоваться время на завершение перед выходом;
- Передавайте только контекст, без функции отмены. Контроль за завершением контекста должен оставаться на вызывающей стороне, иначе логика приложения может стать очень запутанной.