Назад к вопросам
Senior
148
questionbank

Как устроены механизмы async и await под капотом?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Async/await в Swift построен на основе structured concurrency и использует Dispatch/Global Actors. Под капотом работают следующие механизмы:

  1. Continuation Style: Асинхронные функции компилируются в стиль продолжений. Вместо немедленного возврата значения, управление передается в рантайм Swift, который сохраняет текущее состояние функции (локальные переменные, указатель исполнения). Когда асинхронная операция завершается, рантайм возобновляет выполнение функции с сохраненного состояния.

  2. Task: Каждое вызов async функции создает или присоединяется к структурированной Task. Task представляет единицу работы и формирует иерархию. Родительская Task ожидает завершения дочерних Task.

  3. Job: Task разбивается на более мелкие единицы работы, называемые Job. Job представляет собой фрагмент кода, который может быть выполнен на исполнителе (Executor).

  4. Executor: Исполнитель отвечает за запуск Job. В стандартной библиотеке используются глобальные DispatchQueue как исполнители для большинства асинхронных задач. Специфические контексты (например, MainActor) имеют свои специализированные исполнители.

  5. Suspension Points: await является точкой приостановки (suspension point). При достижении await, функция приостанавливается, управление передается вызывающему коду или исполнителю, а текущий Job завершается. Рантайм Swift сохраняет состояние функции.

  6. Resumption: Когда асинхронная операция, на которую ожидал await, завершается, рантайм Swift создает новый Job для продолжения выполнения приостановленной функции. Этот Job ставится в очередь на исполнитель.

  7. 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.