Sobes.tech
Senior

Как работят механизмите async и await под капака?

sobes.tech AI

Отговор от AI

Async/await в Swift е базиран на структурирана конкуренция и използва Dispatch/Global Actors. Механизмите, които работят под капака, са:

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

  2. Task: Всяко извикване на async функция създава или се присъединява към структурирана Task. Task представлява единица работа и формира йерархия. Родителският Task очаква завършването на дъщерните Task.

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

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

  5. Точки на спиране: await е точка на спиране (suspension point). При достигане на await, функцията се спира, управлението се прехвърля към извикващия код или изпълнителя, а текущият Job приключва. Swift runtime запазва състоянието на функцията.

  6. Възобновяване: Когато асинхронната операция, на която е очаквал await, приключи, Swift runtime създава нов Job за продължаване на изпълнението на спрената функция. Този Job се поставя в опашката на изпълнителя.

  7. Отмяна: Структурираната конкуренция поддържа йерархично отмяна. Отмяната на родителски 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.