Sobes.tech
Senior

Kuidas töötavad async ja await mehhanismid kapoti all?

sobes.tech AI

Vastus AI-lt

Swiftis põhineb Async/await struktureeritud konkurentsil ning kasutab Dispatch/Global Actors. Põhjalikumalt töötavad järgmised mehhanismid:

  1. Jätkamisstiil: Asünkroonfunktsioonid kompileeritakse jätkamistiiliks. Selle asemel, et kohe väärtust tagastada, edastatakse juhtimine Swift'i jooksutajale, mis salvestab funktsiooni praeguse oleku (kohalikud muutujad, täitmise näitaja). Kui asünkroonne operatsioon lõpeb, jätkab jooksutaja funktsiooni salvestatud olekust.

  2. Ülesanne: Iga async funktsiooni väljakutse loob või ühineb struktureeritud Task-ga. Task esindab tööühikut ning loob hierarhia. Vanem-Task ootab, kuni alam-Task-id on lõpetatud.

  3. Töö: Task jaguneb väiksemateks tööüksusteks, mida nimetatakse Job-deks. Job on koodilõik, mida saab täita täitjal (Executor).

  4. Täitev: Täitja vastutab Job-i käivitamise eest. Standardraamatukogus kasutatakse peamiselt globaalset DispatchQueue-d täitjatena enamikule asünkroonsetele ülesannetele. Spetsiifilised kontekstid (näiteks MainActor) kasutavad oma spetsialiseeritud täitjaid.

  5. Arestuspunktid: await on peatamispunkt. Kui jõutakse await-i, peatub funktsioon, juhtimine antakse väljakutsele või täitjale ning praegune Job lõpetatakse. Swift'i jooksutaja salvestab funktsiooni oleku.

  6. Taaskäivitamine: Kui oodatud asünkroonne operatsioon lõpeb, loob Swift'i jooksutaja uue Job jätkamiseks peatatud funktsiooni. See Job lisatakse täitja järjekorda.

  7. Tühistamine: Struktureeritud konkurents toetab hierarhilist tühistamist. Vanem-Task tühistamine tühistab automaatselt kõik selle lapsed-Task-id. Asünkroonsetel operatsioonidel võib olla kontroll tühistamise staatuse üle ning reageerida vastavalt.

Siin on lihtsustatud näide async funktsiooni kompileerimisest:

// Algne async funktsioon
func fetchData() async -> Data {
    // Asünkroonne operatsioon
    let data = await URLSession.shared.data(from: url).0
    return data
}

// Lihtsustatud kompileerimise näide
// Selle asemel, et tagastada, funktsioon võtab vastu Continuation
func fetchData(_ continuation: Continuation<Data, Error>) {
    // Funktsiooni algus
    let url = URL(string: "...")! // Näide
    
    // Asünkroonse operatsiooni käivitamine
    URLSession.shared.data(from: url) { data, _, error in
        // See sulg täidetakse pärast asünkroonse operatsiooni lõppu
        if let data = data {
            // Saadetakse tulemus continuation-ile, jätkates funktsiooni
            continuation.resume(returning: data)
        } else if let error = error {
            continuation.resume(throwing: error)
        } else {
            // Muud juhtumid
            continuation.resume(throwing: NSError(domain: "", code: 0, userInfo: nil))
        }
    }
    // Funktsioon lõpeb siin, ootab `resume`-i kutsumist continuation-ilt
}

Await on sisuliselt süntaksisuhkur Continuation-i haldamise ja Job-i planeerimise üle täitjal. Peatamine ei blokeeri otseselt niidi, vaid võimaldab täitjal täita teisi Job-e.