Jak działają mechanizmy async i await od kuchni?
sobes.tech AI
Odpowiedź od AI
Async/await w Swift opiera na oparciu o strukturalną współbieżność i korzysta z Dispatch/Global Actors. Mechanizmy działające pod maską to:
-
Styl kontynuacji: Funkcje asynchroniczne są kompilowane w stylu kontynuacji. Zamiast od razu zwracać wartość, kontrola jest przekazywana do środowiska wykonawczego Swift, które zapisuje aktualny stan funkcji (zmienne lokalne, wskaźnik wykonania). Gdy operacja asynchroniczna się kończy, środowisko wykonawcze wznawia funkcję od zapisanego stanu.
-
Zadanie: Każde wywołanie funkcji
asynctworzy lub dołącza do strukturalnegoTask.Taskreprezentuje jednostkę pracy i tworzy hierarchię. Zadanie nadrzędne oczekuje na zakończenie zadań podrzędnych. -
Job:
Taskjest dzielone na mniejsze jednostki pracy zwaneJob.Jobto fragment kodu, który może być wykonany na wykonawcy. -
Wykonawca: Odpowiada za uruchomienie
Job. W bibliotece standardowej używa się globalnychDispatchQueuejako wykonawców dla większości zadań asynchronicznych. Konteksty specyficzne (np.MainActor) mają własnych wyspecjalizowanych wykonawców. -
Punkty zawieszenia:
awaitjest punktem zawieszenia. Po osiągnięciu, funkcja jest zawieszana, kontrola jest przekazywana do wywołującego kodu lub wykonawcy, a bieżącyJobkończy się. Środowisko wykonawcze Swift zapisuje stan funkcji. -
Wznowienie: Gdy operacja asynchroniczna oczekiwana przez
awaitkończy się, środowisko wykonawcze Swift tworzy nowyJobdo kontynuacji zawieszonej funkcji. TenJobtrafia do kolejki wykonawcy. -
Anulowanie: Strukturalna współbieżność obsługuje hierarchiczne anulowanie. Anulowanie nadrzędnego
Taskautomatycznie anuluje wszystkie jego podrzędneTask. Operacje asynchroniczne mogą sprawdzać status anulowania i odpowiednio reagować.
Oto uproszczony przykład kompilacji funkcji async:
// Oryginalna funkcja async
func fetchData() async -> Data {
// Operacja asynchroniczna
let data = await URLSession.shared.data(from: url).0
return data
}
// Uproszczona reprezentacja kompilacji
// Zamiast zwracać, funkcja przyjmuje Continuation
func fetchData(_ continuation: Continuation<Data, Error>) {
// Kod początkowy funkcji
let url = URL(string: "...")! // Przykład
// Rozpoczęcie operacji asynchronicznej
URLSession.shared.data(from: url) { data, _, error in
// Ten blok jest wykonywany po zakończeniu operacji asynchronicznej
if let data = data {
// Przekazanie wyniku do continuation, wznowienie funkcji
continuation.resume(returning: data)
} else if let error = error {
continuation.resume(throwing: error)
} else {
// Obsługa innych przypadków
continuation.resume(throwing: NSError(domain: "", code: 0, userInfo: nil))
}
}
// Funkcja kończy się tutaj, oczekując na wywołanie resume od continuation
}
Await jest w zasadzie cukrem składniowym nad zarządzaniem Continuation i planowaniem Job na wykonawcy. Zawieszenie nie blokuje bezpośrednio wątku, a pozwala wykonawcy realizować inne Job.