Sobes.tech
Senior

Come funzionano i meccanismi async e await sotto il cofano?

sobes.tech AI

Risposta dell'AI

Async/await in Swift si basa sulla concorrenza strutturata e utilizza Dispatch/Global Actors. I meccanismi sottostanti sono:

  1. Stile di Continuation: Le funzioni asincrone vengono compilate in stile di continuazione. Invece di restituire immediatamente un valore, il controllo viene trasferito al runtime Swift, che salva lo stato corrente della funzione (variabili locali, puntatore di esecuzione). Quando l'operazione asincrona termina, il runtime riprende l'esecuzione della funzione dallo stato salvato.

  2. Task: Ogni chiamata a una funzione async crea o si unisce a una Task strutturata. La Task rappresenta un'unità di lavoro e forma una gerarchia. La Task genitore aspetta che le Task figlie terminino.

  3. Job: La Task viene suddivisa in unità di lavoro più piccole chiamate Job. Un Job rappresenta un frammento di codice che può essere eseguito su un esecutore.

  4. Esecutore: L'esecutore è responsabile di avviare il Job. Nella libreria standard, si usano DispatchQueue globali come esecutori per la maggior parte delle attività asincrone. I contesti specifici (ad esempio, MainActor) hanno i propri esecutori specializzati.

  5. Punti di sospensione: await è un punto di sospensione. Quando viene raggiunto, la funzione si sospende, il controllo viene trasferito al codice o esecutore chiamante, e il Job corrente termina. Il runtime Swift salva lo stato della funzione.

  6. Ripresa: Quando l'operazione asincrona attesa con await termina, il runtime Swift crea un nuovo Job per continuare l'esecuzione della funzione sospesa. Questo Job viene messo in coda sull'esecutore.

  7. Annullamento: La concorrenza strutturata supporta l'annullamento gerarchico. L'annullamento di un Task genitore annulla automaticamente tutti i suoi Task figli. Le operazioni asincrone possono verificare lo stato di annullamento e reagire di conseguenza.

Ecco un esempio semplificato della compilazione di una funzione async:

// Funzione async originale
func fetchData() async -> Data {
    // Operazione asincrona
    let data = await URLSession.shared.data(from: url).0
    return data
}

// Rappresentazione semplificata della compilazione
// Invece di restituire, la funzione accetta una Continuation
func fetchData(_ continuation: Continuation<Data, Error>) {
    // Codice iniziale della funzione
    let url = URL(string: "...")! // Esempio
    
    // Avviare operazione asincrona
    URLSession.shared.data(from: url) { data, _, error in
        // Questa chiusura viene eseguita dopo il completamento dell'operazione asincrona
        if let data = data {
            // Passare il risultato alla continuation, riprendendo la funzione
            continuation.resume(returning: data)
        } else if let error = error {
            continuation.resume(throwing: error)
        } else {
            // Gestione di altri casi
            continuation.resume(throwing: NSError(domain: "", code: 0, userInfo: nil))
        }
    }
    // La funzione termina qui, aspettando la chiamata a resume della continuation
}

Await è fondamentalmente zucchero sintattico sulla gestione di Continuation e sulla pianificazione di Job sull'esecutore. La sospensione non blocca direttamente il thread, ma consente all'esecutore di eseguire altri Job.