async: cosa è, Deferred e come funzionano le coroutine parallele

Autore: IT Sectr Pubblicato: 2026-06-22 Tempo di lettura: 8 min

async — è un Costruttore di Coroutine in Kotlin che avvia una coroutine e restituisce un oggetto Deferred<T> per ottenere il risultato di un’operazione asincrona. async consente di eseguire più attività in parallelo e aggregare i loro risultati tramite await(). Secondo la guida JetBrains, 2024, async è il metodo preferito per organizzare calcoli paralleli nelle coroutine.

Punti Chiave

  • async — un costruttore di coroutine che restituisce Deferred<T> con il risultato di un’operazione asincrona
  • Deferred — una promessa asincrona, erede di Job, con il metodo await() per ottenere il risultato
  • async consente di eseguire attività indipendenti in parallelo, riducendo il tempo totale
  • await() — una funzione suspend che sospende la coroutine fino all’ottenimento del risultato senza bloccare
  • Concorrenza strutturata con async garantisce la cancellazione di tutte le attività parallele in caso di errore

Cos’è async in Kotlin Coroutines?

async — è una funzione di estensione di CoroutineScope che crea una coroutine restituendo un risultato. A differenza di launch, async restituisce Deferred<T> — un oggetto che rappresenta un valore futuro. Una coroutine avviata tramite async viene eseguita concorrentemente con altre coroutine, consentendo di parallelizzare i calcoli.

async viene utilizzato quando il risultato di un’operazione asincrona è necessario per l’elaborazione successiva: caricamento di dati da più fonti, elaborazione in batch di collezioni, richieste parallele a varie API. Deferred viene creato immediatamente e il risultato diventa disponibile dopo il completamento della coroutine.

kotlin
import kotlinx.coroutines.*

suspend fun String.delayedValue(): String {
    delay(1000L)
    return this
}

fun main() = runBlocking {
    val deferred: Deferred<String> = async { "Hello".delayedValue() }
    println(deferred.await())
}

Sintassi di async e Deferred

La firma di async è simile a launch: gli stessi parametri CoroutineContext, CoroutineStart e un blocco suspend. La differenza sta nel tipo di ritorno — Deferred<T> invece di Job, dove T è il tipo di risultato restituito dall’ultima espressione del blocco.

kotlin
public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T>

Deferred<T> — un’interfaccia che eredita Job e aggiunge i metodi await(), getCompleted(), getCompletionExceptionOrNull(). await() è una funzione suspend e può essere chiamata solo all’interno di una coroutine o di un’altra funzione suspend. getCompleted() è un metodo non bloccante che lancia un’eccezione se la coroutine non è ancora stata completata.

Attività parallele con async

Il principale vantaggio di async è la capacità di eseguire attività in parallelo. Invece di attendere ogni operazione sequenzialmente (tempo totale = somma dei tempi), async avvia le coroutine simultaneamente, riducendo il tempo totale alla durata dell’operazione più lunga.

Caricamento parallelo dei dati

Uno scenario tipico — caricare il profilo utente, le sue impostazioni e la cronologia ordini con tre richieste async parallele. Dopo il completamento di tutte e tre, i risultati vengono combinati in un unico modello di dati.

kotlin
suspend fun loadUserDashboard(userId: String): Dashboard = coroutineScope {
    val profile = async { api.getProfile(userId) }
    val settings = async { api.getSettings(userId) }
    val orders = async { api.getRecentOrders(userId) }

    Dashboard(
        profile = profile.await(),
        settings = settings.await(),
        orders = orders.await()
    )
}

La funzione coroutineScope avvia coroutine figlie e attende il loro completamento prima di restituire il risultato. L’intero blocco viene eseguito in parallelo, non sequenzialmente.

Gestione dei risultati Deferred

Deferred supporta diversi metodi per lavorare con il risultato. Oltre a await(), ci sono metodi per controllare lo stato, ottenere un valore completato e aggregare più Deferred tramite le funzioni di utilità di Kotlin Coroutines.

MetodoDescrizioneSuspend?
await()Sospende la coroutine fino all’ottenimento del risultato
getCompleted()Restituisce il risultato se la coroutine è completata (altrimenti Exception)No
getCompletionExceptionOrNull()Restituisce l’eccezione in caso di errore o nullNo
isCompletedVerifica se la coroutine è completataNo

Per aggregare una lista di Deferred, si usa awaitAll() — una funzione che sospende l’esecuzione fino al completamento di tutti i Deferred nella collezione. Se almeno uno si è completato con errore, awaitAll() lancia l’eccezione.

kotlin
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
    return coroutineScope {
        val deferreds = requests.map { async { it() } }
        deferreds.awaitAll()
    }
}

Differenze tra async e launch

La scelta tra async e launch dipende dal fatto che il risultato della coroutine sia necessario. launch restituisce Job ed è adatto per operazioni fire-and-forget, async restituisce Deferred ed è usato per attività che restituiscono dati. In termini di sintassi e parametri, i costruttori sono identici.

  • launch → Job (nessun risultato), async → Deferred<T> (risultato T)
  • launch — attività fire-and-forget (logging, caching, invio analitiche)
  • async — calcoli paralleli (caricamento da più API, elaborazione in batch)
  • Entrambi supportano la concorrenza strutturata e i parametri CoroutineStart
  • await() è disponibile solo per Deferred, launch non fornisce risultato

Regola importante: non usare mai async per fire-and-forget. Se il risultato della coroutine non è necessario, usa launch. async crea un overhead per la creazione di Deferred, che non è giustificato in tali scenari.

Esempi di async nello sviluppo mobile

In Android, async viene utilizzato per operazioni parallele all’interno di ViewModel e UseCases. Scenari tipici: caricare profilo e feed notizie simultaneamente, ottenere meteo da più servizi meteorologici, sincronizzazione batch dei dati con il server.

kotlin
class HomeViewModel : ViewModel() {
    fun loadHomeScreen() {
        viewModelScope.launch {
            val userData = async(Dispatchers.IO) { repository.getUserData() }
            val newsFeed = async(Dispatchers.IO) { repository.getNewsFeed() }
            val notifications = async(Dispatchers.IO) { repository.getNotifications() }

            val state = HomeState(
                user = userData.await(),
                news = newsFeed.await(),
                unread = notifications.await()
            )
            _uiState.update { state }
        }
    }
}

Si raccomanda di avvolgere un gruppo di async in coroutineScope o supervisorScope per isolare gli errori. supervisorScope consente alle restanti coroutine async di continuare anche se una fallisce — utile per operazioni non critiche.

Domande Frequenti

Si può usare async senza await?

Si può, ma è inutile. async senza await() avvia una coroutine ma il suo risultato viene perso. Se il risultato non è necessario, usa launch — non crea Deferred ed è più efficiente in termini di memoria e prestazioni.

Come gestisce async le eccezioni?

Un’eccezione all’interno di async viene memorizzata in Deferred. Chiamare await() lancia questa eccezione. Per verificare senza lanciare, usa getCompletionExceptionOrNull(). Quando si utilizza coroutineScope, un errore annulla tutte le coroutine figlie.

Cos’è awaitAll in Kotlin Coroutines?

awaitAll() — una funzione di estensione per Iterable<Deferred<T>> che sospende la coroutine fino al completamento di tutti i Deferred. Restituisce una lista di risultati. Se almeno un Deferred fallisce, lancia l’eccezione e cancella gli altri.

In cosa async differisce da GlobalScope.async?

Sì. async è una funzione di estensione di CoroutineScope legata allo scope. GlobalScope.async crea una coroutine senza legame con il ciclo di vita — può eseguire indefinitamente. GlobalScope non è raccomandato in Android a causa del rischio di perdite.

Quante coroutine async possono essere avviate per richieste parallele?

Il numero è dettato dal dispatcher: Dispatchers.IO supporta fino a 64 thread simultanei, Dispatchers.Default — fino al numero di core CPU. Per operazioni massive, usa parallelismo limitato tramite Semaphore o mapNotNull con awaitAll.

Riepilogo

  • async — un costruttore di coroutine che restituisce Deferred<T> per l’ottenimento asincrono di risultati
  • Deferred eredita Job e aggiunge i metodi await(), getCompleted(), getCompletionExceptionOrNull()
  • Esecuzione parallela con async riduce il tempo totale alla durata dell’operazione più lunga
  • awaitAll() — un’utilità per attendere tutti i Deferred in una collezione
  • coroutineScope — wrapper raccomandato per un gruppo di async con garanzia di cancellazione
  • Non usare async per fire-and-forget — usa invece launch
  • In Android, async viene utilizzato per il caricamento parallelo in ViewModel con viewModelScope

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