Sobes.tech
Senior

Hogyan működnek az async és await mechanizmusok a háttérben?

sobes.tech MI

Válasz az MI-től

Az Async/await Swiftben a strukturált konkurrencia alapjaira épül, és a Dispatch/Global Actors-t használja. A háttérben működő mechanizmusok a következők:

  1. Folytatási stílus: Az aszinkron funkciók folytatási stílusra fordulnak. Ahelyett, hogy azonnal visszaadnák az értéket, az irányítás átkerül a Swift futtatókörnyezetéhez, amely elmenti a függvény aktuális állapotát (helyi változók, végrehajtási mutató). Amikor az aszinkron művelet befejeződik, a futtatókörnyezet folytatja a függvény végrehajtását a mentett állapotból.

  2. Task: Minden async függvényhívás létrehoz vagy csatlakozik egy strukturált Task-hoz. A Task egy munkafolyamatot képvisel, és hierarchiát alkot. A szülő Task várja a gyermek Task-ok befejezését.

  3. Job: A Task kisebb munkarészekre, azaz Job-okra van bontva. A Job egy kódrészletet képvisel, amely végrehajtható egy végrehajtón (Executor).

  4. Executor: A végrehajtó felelős a Job indításáért. A standard könyvtárban globális DispatchQueue-kat használnak végrehajtóként a legtöbb aszinkron feladathoz. A speciális kontextusok (pl. MainActor) saját dedikált végrehajtóikkal rendelkeznek.

  5. Felfüggesztési pontok: Az await egy felfüggesztési pont. Amikor eléri az await, a függvény szünetel, az irányítás átadódik a hívónak vagy a végrehajtónak, és a jelenlegi Job befejeződik. A Swift futtatókörnyezet elmenti a függvény állapotát.

  6. Újraindítás: Amikor az await-től várt aszinkron művelet befejeződik, a Swift futtatókörnyezet új Job-ot hoz létre a szüneteltetett függvény folytatásához. Ezt a Job-ot sorba állítják a végrehajtóra.

  7. Megszakítás: A strukturált konkurrencia támogatja a hierarchikus megszakítást. A szülő Task megszakítása automatikusan megszakítja az összes gyermek Task-ját. Az aszinkron műveletek ellenőrizhetik a megszakítási státuszt, és ennek megfelelően reagálhatnak.

Íme egy egyszerűsített példája az async függvény fordításának:

// Kiinduló async függvény
func fetchData() async -> Data {
    // Aszinkron művelet
    let data = await URLSession.shared.data(from: url).0
    return data
}

// Egyszerűsített fordítási ábra
// A visszatérés helyett a függvény a Continuation-t fogadja
func fetchData(_ continuation: Continuation<Data, Error>) {
    // A függvény kezdeti kódja
    let url = URL(string: "...")! // Példa
    
    // Aszinkron művelet indítása
    URLSession.shared.data(from: url) { data, _, error in
        // Ez a zárás a művelet befejezése után fut
        if let data = data {
            // Eredmény átadása a continuation-nek, a függvény újraindítása
            continuation.resume(returning: data)
        } else if let error = error {
            continuation.resume(throwing: error)
        } else {
            // Egyéb esetek kezelése
            continuation.resume(throwing: NSError(domain: "", code: 0, userInfo: nil))
        }
    }
    // A függvény itt fejeződik be, várva a resume hívást a continuation-től
}

Az Await lényegében szintaktikus cukor a Continuation kezelése és a Job ütemezése felett a végrehajtón. A szünet nem blokkolja közvetlenül a szálat, hanem lehetővé teszi a végrehajtónak, hogy más Job-okat hajtson végre.