Sobes.tech
Senior

Ako fungujú mechanizmy async a await na pozadí?

sobes.tech AI

Odpoveď od AI

Async/await v Swifte je postavený na štruktúrovanej konkurencii a využíva Dispatch/Global Actors. Mechanizmy, ktoré bežia na pozadí, sú:

  1. Štýl pokračovania: Asynchrónne funkcie sú kompilované do štýlu pokračovania. Namiesto okamžitého vrátenia hodnoty sa riadenie prenáša na Swift runtime, ktorý uloží aktuálny stav funkcie (lokálne premenné, ukazovateľ vykonávania). Keď asynchrónna operácia skončí, runtime obnoví vykonávanie funkcie zo stavu, ktorý bol uložený.

  2. Task: Každé volanie async funkcie vytvorí alebo sa pripojí ku štruktúrovanej Task. Task predstavuje jednotku práce a tvorí hierarchiu. Rodičovský Task čaká na dokončenie dcérskych Task.

  3. Job: Task sa rozdeľuje na menšie jednotky práce, nazývané Job. Job predstavuje fragment kódu, ktorý môže byť vykonaný na vykonávateľovi (Executor).

  4. Executor: Vykonávateľ je zodpovedný za spustenie Job. V štandardnej knižnici sa používajú globálne DispatchQueue ako vykonávateľov pre väčšinu asynchrónnych úloh. Špeciálne kontexty (napríklad MainActor) majú svoje špecializované vykonávateľov.

  5. Bodky pozastavenia: await je bod pozastavenia (suspension point). Pri dosiahnutí await sa funkcia pozastaví, riadenie sa prenesie na volajúci kód alebo vykonávateľa, a aktuálny Job sa ukončí. Swift runtime uloží stav funkcie.

  6. Obnovenie: Keď asynchrónna operácia, na ktorú await čakal, skončí, Swift runtime vytvorí nový Job na pokračovanie vykonávania pozastavenej funkcie. Tento Job sa umiestni do fronty na vykonávateľovi.

  7. Zrušenie: Štruktúrovaná konkurencia podporuje hierarchické zrušenie. Zrušenie rodičovského Task automaticky zruší všetky jeho dcérske Task. Asynchrónne operácie môžu kontrolovať stav zrušenia a reagovať primerane.

Tu je zjednodušený príklad kompilácie async funkcie:

// Počiatočná async funkcia
func fetchData() async -> Data {
    // Asynchrónna operácia
    let data = await URLSession.shared.data(from: url).0
    return data
}

// Zjednodušené zobrazenie kompilácie
// Namiesto návratu funkcia prijíma Continuation
func fetchData(_ continuation: Continuation<Data, Error>) {
    // Počiatočný kód funkcie
    let url = URL(string: "...")! // Príklad
    
    // Spustenie asynchrónnej operácie
    URLSession.shared.data(from: url) { data, _, error in
        // Tento blok sa vykoná po dokončení asynchrónnej operácie
        if let data = data {
            // Prenos výsledku do continuation, obnovenie funkcie
            continuation.resume(returning: data)
        } else if let error = error {
            continuation.resume(throwing: error)
        } else {
            // Spracovanie iných prípadov
            continuation.resume(throwing: NSError(domain: "", code: 0, userInfo: nil))
        }
    }
    // Funkcia končí tu, čaká na volanie resume od continuation
}

Await je v podstate syntaktický cukor nad správou Continuation a plánovaním Job na vykonávateľovi. Pozastavenie neblokuje priamo vlákno, ale umožňuje vykonávateľovi vykonávať iné Job-y.