Как устроены механизмы async и await под капотом?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Async/await в Swift построен на основе structured concurrency и использует Dispatch/Global Actors. Под капотом работают следующие механизмы:
-
Continuation Style: Асинхронные функции компилируются в стиль продолжений. Вместо немедленного возврата значения, управление передается в рантайм Swift, который сохраняет текущее состояние функции (локальные переменные, указатель исполнения). Когда асинхронная операция завершается, рантайм возобновляет выполнение функции с сохраненного состояния.
-
Task: Каждое вызов
asyncфункции создает или присоединяется к структурированнойTask.Taskпредставляет единицу работы и формирует иерархию. РодительскаяTaskожидает завершения дочернихTask. -
Job:
Taskразбивается на более мелкие единицы работы, называемыеJob.Jobпредставляет собой фрагмент кода, который может быть выполнен на исполнителе (Executor). -
Executor: Исполнитель отвечает за запуск
Job. В стандартной библиотеке используются глобальныеDispatchQueueкак исполнители для большинства асинхронных задач. Специфические контексты (например,MainActor) имеют свои специализированные исполнители. -
Suspension Points:
awaitявляется точкой приостановки (suspension point). При достиженииawait, функция приостанавливается, управление передается вызывающему коду или исполнителю, а текущийJobзавершается. Рантайм Swift сохраняет состояние функции. -
Resumption: Когда асинхронная операция, на которую ожидал
await, завершается, рантайм Swift создает новыйJobдля продолжения выполнения приостановленной функции. ЭтотJobставится в очередь на исполнитель. -
Cancellation: Structured concurrency поддерживает иерархическую отмену. Отмена родительской
Taskавтоматически отменяет все ее дочерниеTask. Асинхронные операции могут проверять статус отмены и реагировать соответствующим образом.
Вот упрощенный пример компиляции async функции:
// Исходная async функция
func fetchData() async -> Data {
// Асинхронная операция
let data = await URLSession.shared.data(from: url).0
return data
}
// Упрощенное представление компиляции
// Вместо возврата, функция принимает Continuation
func fetchData(_ continuation: Continuation<Data, Error>) {
// Начальный код функции
let url = URL(string: "...")! // Пример
// Запуск асинхронной операции
URLSession.shared.data(from: url) { data, _, error in
// Этот замыкание выполняется после завершения асинхронной операции
if let data = data {
// Передаем результат в continuation, возобновляя функцию
continuation.resume(returning: data)
} else if let error = error {
continuation.resume(throwing: error)
} else {
// Обработка других случаев
continuation.resume(throwing: NSError(domain: "", code: 0, userInfo: nil))
}
}
// Функция завершается здесь, ожидая вызова resume от continuation
}
Await по сути является синтаксическим сахаром над управлением Continuation и планированием Job на исполнителе. Приостановка не блокирует поток напрямую, а позволяет исполнителю выполнять другие Job.