Sobes.tech
Senior

Jak fungují mechanismy async a await na pozadí?

sobes.tech AI

Odpověď od AI

Async/await ve Swiftu je postaven na strukturované konkurenci a využívá Dispatch/Global Actors. Mechanismy, které běží na pozadí, jsou:

  1. Styl pokračování: Asynchronní funkce jsou kompilovány do stylu pokračování. Místo okamžitého vrácení hodnoty se řízení předává runtime Swift, který ukládá aktuální stav funkce (lokální proměnné, ukazatel vykonávání). Když asynchronní operace skončí, runtime obnoví vykonávání funkce ze stavu, který byl uložen.

  2. Task: Každé volání async funkce vytvoří nebo se připojí ke strukturovanému Task. Task představuje jednotku práce a vytváří hierarchii. Rodičovský Task čeká na dokončení podřízených Task.

  3. Job: Task je rozdělen na menší jednotky práce nazývané Job. Job představuje fragment kódu, který může být vykonán na vykonavateli (Executor).

  4. Executor: Vykonavatel je zodpovědný za spuštění Job. Ve standardní knihovně jsou jako vykonavatelé používány globální DispatchQueue pro většinu asynchronních úloh. Specifické kontexty (například MainActor) mají své specializované vykonavatele.

  5. Suspension Points: await je bod pozastavení (suspension point). Při dosažení await se funkce pozastaví, řízení se předá volajícímu kódu nebo vykonavateli a aktuální Job se ukončí. Runtime Swift uloží stav funkce.

  6. Resumption: Když asynchronní operace, na kterou await čekal, skončí, runtime Swift vytvoří nový Job pro pokračování vykonávání pozastavené funkce. Tento Job je zařazen do fronty na vykonavateli.

  7. Cancellation: Strukturovaná konkurence podporuje hierarchické zrušení. Zrušení rodičovského Task automaticky zruší všechny její podřízené Task. Asynchronní operace mohou kontrolovat stav zrušení a reagovat odpovídajícím způsobem.

Zde je zjednodušený příklad kompilace async funkce:

// Výchozí async funkce
func fetchData() async -> Data {
    // Asynchronní operace
    let data = await URLSession.shared.data(from: url).0
    return data
}

// Zjednodušené zobrazení kompilace
// Místo návratu funkce přijímá Continuation
func fetchData(_ continuation: Continuation<Data, Error>) {
    // Počáteční kód funkce
    let url = URL(string: "...")! // Příklad
    
    // Spuštění asynchronní operace
    URLSession.shared.data(from: url) { data, _, error in
        // Tento blok se provádí po dokončení asynchronní operace
        if let data = data {
            // Předání výsledku do continuation, obnovení funkce
            continuation.resume(returning: data)
        } else if let error = error {
            continuation.resume(throwing: error)
        } else {
            // Zpracování jiných případů
            continuation.resume(throwing: NSError(domain: "", code: 0, userInfo: nil))
        }
    }
    // Funkce končí zde, čeká na volání resume od continuation
}

Await je v podstatě syntaktický cukr nad správou Continuation a plánováním Job na vykonavateli. Pozastavení přímo nezablokuje vlákno, ale umožňuje vykonavateli vykonávat jiné Job.