Async/Await: essenza, funzioni async e lavoro con await

Autore: IT Sectr Pubblicato: 2026-03-16 Tempo di lettura: 8 min

Async/Await è una coppia di parole chiave per scrivere codice asincrono in stile sincrono, disponibile in Swift da iOS 13 e in Kotlin con le coroutine. Secondo Apple Swift Documentation, 2026, Async/Await sostituisce le catene di callback e GCD, rendendo il codice asincrono lineare e leggibile. La parola chiave async marca una funzione come asincrona, e await sospende la sua esecuzione fino al ricevimento del risultato.

Punti Chiave

  • Async/Await è zucchero sintattico per codice asincrono senza callback annidate
  • async marca una funzione come asincrona e sospendibile
  • await sospende l'esecuzione della funzione fino al completamento dell'operazione asincrona
  • Task in Swift crea una nuova unità di lavoro asincrona con contesto
  • Structured Concurrency garantisce che tutte le attività figlie vengano completate prima della genitore

Cos'è Async/Await e la programmazione asincrona

Async/Await è una costruzione del linguaggio che permette di scrivere codice asincrono in modo lineare quanto il codice sincrono. In Swift, è apparso in iOS 13 / macOS 10.15 insieme al framework Swift Concurrency. In Kotlin, async/await è disponibile attraverso la libreria delle coroutine (kotlinx.coroutines) a partire dalla versione 1.3.

Prima di async/await, il codice asincrono era costruito su callback, GCD o RxSwift. Le catene di callback annidate portavano all'inferno delle callback — un annidamento profondo che complica la lettura e il debug. Async/Await risolve questo problema permettendo alle funzioni asincrone di “sospendersi” nei punti await e riprendere dopo aver ricevuto il risultato.

Secondo Google (2025), usare async/await in Kotlin riduce il numero di righe di codice asincrono del 40–60% rispetto all'approccio con callback. In Swift, l'adozione di Swift Concurrency ha raggiunto il 68% tra le applicazioni pubblicate nel 2025. iOS 16 ha aggiunto il supporto di Swift Concurrency in UIKit, SwiftUI e Foundation, rendendo async/await lo strumento standard per tutti i nuovi progetti Apple.

Principio di funzionamento: funzioni sospendibili

La differenza principale tra async/await e i thread è la sospensione senza blocco. Quando una funzione incontra await, si sospende, liberando il thread corrente per altre attività. Dopo il completamento dell'operazione asincrona, la funzione riprende sullo stesso thread o su un thread diverso. Questo si chiama multitasking cooperativo.

Come funziona Async/Await: sospensione e ripresa

Una funzione async viene compilata in una macchina a stati — un automa finito che gestisce gli stati di esecuzione. Al punto await, il compilatore salva il contesto (variabili locali, indirizzo di ritorno) e passa il controllo al runtime. Quando il risultato è pronto, il runtime ripristina il contesto e continua l'esecuzione.

In Swift, questa macchina a stati è implementata a livello del compilatore SIL (Swift Intermediate Language). In Kotlin, viene utilizzato il meccanismo Continuation Passing Style (CPS): ogni funzione suspend accetta un parametro Continuation nascosto attraverso il quale viene restituito il risultato. Entrambi gli approcci garantiscono una ripresa sicura senza perdite di memoria.

  • Swift — il compilatore traduce la funzione async in SIL con un frame coroutine runAsync
  • Kotlin — ogni funzione suspend riceve un Continuation e delega l'esecuzione al dispatcher
  • Entrambi i linguaggi — liberano il thread in await, senza bloccarlo durante l'attesa
  • Eccezioni — vengono propagate tramite meccanismi standard (throws / try-catch)

Async/Await vs Callback e GCD: confronto degli approcci

L'approccio con callback richiede il passaggio di una closure che viene chiamata dopo il completamento dell'operazione. Con tre richieste sequenziali, si ottengono tre livelli di annidamento con gestione degli errori a ogni livello. Async/Await trasforma quelle stesse tre richieste in tre righe sequenziali con un singolo blocco catch.

GCD (DispatchQueue) risolve l'inferno delle callback attraverso code seriali e DispatchGroup, ma rimane verboso. Per un compito semplice — caricare utente, poi amici, poi UI — sono necessarie code, gruppi e blocchi notify. Async/Await esegue lo stesso compito con tre righe in un'unica funzione.

AspettoCallbackGCDAsync/Await
AnnidamentoProfondo (piramide della perdizione)Medio (gruppi+notify)Lineare (piatto)
Gestione erroriPer callbackManualeUnificata (try/catch)
AnnullamentoManualeLimitatoTask.cancel()
Thread safetyManualeTramite coda serialeTramite MainActor

Secondo Apple WWDC 2024, Swift Concurrency async/await è l'approccio raccomandato per il nuovo codice. GCD rimane per l'integrazione con librerie C e scenari specifici di basso livello di gestione dei thread.

Swift Concurrency: Task, TaskGroup e Actor

Task è l'unità base del lavoro asincrono in Swift. Un Task viene creato nel contesto di un attore esistente o su un thread arbitrario. All'interno di un Task, è possibile chiamare funzioni async tramite await. TaskGroup permette di avviare più attività figlie in parallelo e raccogliere i loro risultati — un analogo potenziato di DispatchGroup.

Actor è un tipo di riferimento thread-safe che protegge il suo stato dalle race condition. Il compilatore garantisce che l'accesso alle proprietà dell'attore sia possibile solo tramite chiamate async o all'interno dell'isolamento dell'attore. Actor sostituisce la DispatchQueue seriale per proteggere lo stato mutabile condiviso senza blocchi manuali.

Structured Concurrency

Il principio della Structured Concurrency afferma: ogni attività asincrona ha un genitore, e il genitore non completa finché tutti i suoi figli non sono completati. Se l'attività A avvia Task { await B() }, allora A aspetta B. Questo previene perdite di attività e garantisce un ciclo di vita prevedibile.

Async/Await in Kotlin con le coroutine

In Kotlin, async/await è implementato tramite coroutine — thread leggeri del linguaggio. Una funzione viene marcata con la parola chiave suspend (analoga a async di Swift). Per avviare, si usa launch (spara e dimentica) o async (con risultato). Await viene chiamato per ottenere il risultato da async.

Dispatcher determina su quale pool di thread viene eseguita la coroutine: Dispatchers.Main per l'interfaccia utente, Dispatchers.IO per rete/disco e Dispatchers.Default per attività intensive di CPU. Structured Concurrency è garantita tramite CoroutineScope — quando l'ambito viene annullato, tutte le coroutine figlie vengono annullate.

Secondo JetBrains (2025), il 97% delle app Android su Google Play usa kotlinx.coroutines, e l'82% di esse impiega async/await per le richieste di rete. Le coroutine sono diventate lo standard de facto per la programmazione asincrona in Android. Kotlin Multiplatform supporta anche le coroutine, permettendo l'uso di codice asincrono condiviso su Android, iOS e lato server.

Esempi di codice con Async/Await in Swift e Kotlin

Vediamo due esempi per ogni linguaggio: caricamento sequenziale dei dati e richieste parallele con TaskGroup o async/await.

Swift: Chiamate async sequenziali

await sospende l'esecuzione fino al ricevimento dei dati utente, poi fino al ricevimento degli amici. Nessuna closure annidata.

swift
func loadProfile() async throws -> Profile {
    let user = try await api.fetchUser()
    let friends = try await api.fetchFriends(for: user.id)
    return Profile(user: user, friends: friends)
}

Swift: Richieste parallele con TaskGroup

TaskGroup avvia attività in parallelo e raccoglie i risultati in un array. L'ordine dei risultati potrebbe non corrispondere all'ordine di avvio.

swift
func loadParallel() async throws -> [String] {
    await withThrowingTaskGroup(of: String.self) { group in
        group.addTask { try await api.fetchName() }
        group.addTask { try await api.fetchEmail() }
        group.addTask { try await api.fetchAvatar() }
        var results = [String]()
        for try await value in group { results.append(value) }
        return results
    }
}

Kotlin: Chiamata sequenziale con async/await

Le funzioni suspend possono chiamare altre funzioni suspend. Analogo a Swift: chiamate sequenziali.

kotlin
suspend fun loadProfile(): Profile {
    val user = api.fetchUser()
    val friends = api.fetchFriends(user.id)
    return Profile(user, friends)
}

Kotlin: Richieste parallele con async/await

In Kotlin, il parallelismo si ottiene tramite coroutineScope con async per ogni richiesta.

kotlin
suspend fun loadParallel(): List<String> = coroutineScope {
    val name = async { api.fetchName() }
    val email = async { api.fetchEmail() }
    val avatar = async { api.fetchAvatar() }
    listOf(name.await(), email.await(), avatar.await())
}

Errori comuni e limitazioni di Async/Await

Task { } dimenticato — una funzione async non può essere chiamata da un contesto sincrono senza essere avvolta in un Task. In Swift, questo viene compilato con errore. In Kotlin, richiede l'avvio tramite lifecycleScope o viewModelScope. Tentare di chiamare una funzione suspend da una funzione normale non compilerà.

Uso eccessivo di async senza necessità

Non tutte le funzioni devono essere asincrone. Le attività intensive di CPU senza I/O non traggono vantaggio da async — è meglio eseguirle su DispatchQueue con QoS .userInitiated. Async/Await è ottimizzato per operazioni legate a I/O: rete, disco, attesa.

Blocco del thread principale tramite .result

Usare .result sul thread principale (attesa sincrona del risultato di una funzione async) porta al blocco. In Swift, Task.synchronousWait non è raccomandato da Apple. In Kotlin, runBlocking sul thread principale è un antipattern — usa lifecycleScope.

Perdita di Task in annullamento

Se un Task non viene salvato in una proprietà della classe, dopo aver lasciato il contesto viene annullato. In Swift, Task ha ambito — il suo ciclo di vita è legato al contesto creatore. Salva il Task in una proprietà se necessiti di un'operazione di lunga durata.

Domande Frequenti

In che cosa async/await differisce da DispatchQueue?

Async/Await è una costruzione del linguaggio che non richiede creare code. DispatchQueue è un'API di sistema per gestire i thread. Async/await viene compilato in una macchina a stati a livello di linguaggio, GCD — in syscall a livello di sistema operativo.

await blocca il thread?

No — await sospende la funzione ma libera il thread per altre attività. Dopo il completamento dell'operazione, la funzione riprende su qualsiasi thread disponibile dal pool.

Si può annullare un'attività async?

Sì — tramite Task.cancel() in Swift o Job.cancel() in Kotlin. L'annullamento si propaga a tutte le attività figlie secondo Structured Concurrency. Il codice verifica il flag di annullamento tramite Task.isCancelled o ensureActive().

Cos'è MainActor in Swift?

MainActor è un attore le cui proprietà e metodi vengono sempre eseguiti sul thread principale. Marcando una funzione con @MainActor, garantisci che gli aggiornamenti dell'interfaccia utente avvengano sul thread corretto, sostituendo DispatchQueue.main.async.

In quali linguaggi esiste async/await?

Async/Await è disponibile in Swift (iOS 13+), Kotlin (tramite kotlinx.coroutines), Dart (Flutter), JavaScript/TypeScript, Python, C#, Rust e Go (tramite goroutine con zucchero sintattico).

Riepilogo

  • Async/Await è una costruzione del linguaggio per codice asincrono lineare senza inferno delle callback
  • Swift Concurrency fornisce Task, TaskGroup, Actor e Structured Concurrency
  • Le coroutine di Kotlin usano funzioni suspend, Dispatcher e ambiti di coroutine
  • La sospensione senza blocco è la differenza principale di async/await rispetto a thread e code
  • Structured Concurrency garantisce che tutte le attività figlie vengano completate prima della genitore
  • Async/Await vs GCD — il primo è preferibile per codice nuovo, il secondo per legacy e basso livello
  • MainActor in Swift e Dispatchers.Main in Kotlin proteggono il thread dell'interfaccia utente da operazioni in background

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche