Каждая задача (Task) в
Swift может иметь свой собственный контекст - локальные данные, доступные ей и ее дочерним задачам. Это похоже на
ThreadLocal, но для асинхронных задач.
Как это работает:
- @TaskLocal: проперти-враппер, который позволяет шарить данные между задачами.
- withValue: задаёт значение на время выполнения блока кода.
- Наследование: дочерние задачи (async let, TaskGroup) получают копию контекста родителя.
Пример:
@TaskLocal static var requestID: String?
func fetchData() async {
print("Request ID:", requestID ?? "unknown") // Доступ к контексту
}
Task {
await $requestID.withValue("123") {
await fetchData() // Выведет "123"
}
await fetchData() // Выведет "unknown" так как контекст сброшен
}
Что под капотом:
- Хранилище: словарь стеков (каждый ключ - свой стек значений).
- withValue: пушит значение в стек, defer - удаляет после выполнения.
- Sendable-типы: гарантируют потокобезопасность.
Ограничения:
- Не для больших данных: только для легковесного контекста (ID, флаги).
- Изменяемые объекты: если передаёте класс, изменения будут видны во всех задачах.
Вывод:
Task Local отлично подходит для передачи контекстных данных в асинхронных операциях - например, для логирования
ID запросов, управления настройками выполнения или отладки цепочек задач. Главное помнить, что это инструмент для легковесных данных, а не для сложных изменяемых объектов.