0Pricing
Kotlin Academy · Lezione

Propagazione delle eccezioni con async/await

Comprenda come si propagano le eccezioni da async e quando usare try-await.

Propagazione delle eccezioni con async/await è una lezione Kotlin Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Kotlin Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Kotlin Academy include 4 lezioni in totale.

async memorizza le eccezioni

A differenza di launch, async memorizza l'eccezione nel Deferred restituito. L'eccezione viene rilanciata solo quando si chiama await().

import kotlinx.coroutines.*
fun main() = runBlocking {
    val deferred = async {
        throw RuntimeException("async error")
    }
    try {
        deferred.await()  // exception rethrown here
    } catch (e: RuntimeException) {
        println("Caught: ${e.message}")
    }
}

Eccezione senza await()

Se non si chiama mai await(), con un padre Job normale l'eccezione viene scartata silenziosamente. È preferibile usare supervisorScope + attendere sempre il risultato.

import kotlinx.coroutines.*
fun main() = runBlocking {
    // Exception stored in deferred, never retrieved:
    val d = async { throw RuntimeException("lost exception") }
    delay(100)  // d has failed — exception never surfaced
    println("d.isCancelled: ${d.isCancelled}")
}

async in coroutineScope

All'interno di un coroutineScope normale, se un figlio async genera un'eccezione e questa si propaga (perché non viene catturata), annulla lo scope e tutti i fratelli.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {
            val a = async { "A" }
            val b = async { throw RuntimeException("B failed") }
            println(a.await())
            println(b.await())  // propagates, cancels scope
        }
    } catch (e: RuntimeException) {
        println("Scope failed: ${e.message}")
    }
}

async in supervisorScope

All'interno di supervisorScope, le eccezioni provenienti da async NON si propagano ai fratelli. Ogni await()` deve essere gestito singolarmente.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val a = async { "A" }
        val b = async { throw RuntimeException("B failed") }
        println(a.await())
        try { println(b.await()) }
        catch (e: RuntimeException) { println("b failed: ${e.message}") }
    }
}

awaitAll per più async

awaitAll(d1, d2, d3) attende tutti i deferred. Se uno qualsiasi fallisce, genera immediatamente un'eccezione e annulla gli altri.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        val results = awaitAll(
            async { "result1" },
            async { throw RuntimeException("task 2 failed") },
            async { "result3" }
        )
        println(results)
    } catch (e: Exception) {
        println("awaitAll failed: ${e.message}")
    }
}

runCatching con async

Racchiuda ogni await() in runCatching per raccogliere risultati ed errori senza interrompere l'elaborazione al primo errore.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val deferreds = listOf(
            async { "A" },
            async { throw RuntimeException("B") },
            async { "C" }
        )
        val results = deferreds.map { runCatching { it.await() } }
        results.forEach { println(it) }
    }
}

Attività parallele con accumulo degli errori

Raccolga tutti gli errori delle attività async parallele e li segnali insieme, invece di interrompere l'elaborazione al primo errore.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val tasks = listOf("a", "b", "c")
    val results = supervisorScope {
        tasks.map { t ->
            async {
                if (t == "b") throw RuntimeException("b failed")
                t.uppercase()
            }
        }.map { runCatching { it.await() } }
    }
    val errors = results.filter { it.isFailure }
    val successes = results.mapNotNull { it.getOrNull() }
    println("Success: $successes, Errors: ${errors.size}")
}

Conservazione del tipo di eccezione

Le eccezioni generate all'interno di async conservano il proprio tipo. Catturi il tipo specifico di eccezione in corrispondenza di await() per una gestione precisa degli errori.

import kotlinx.coroutines.*
class NetworkException(msg: String) : RuntimeException(msg)
fun main() = runBlocking {
    val d = async { throw NetworkException("timeout") }
    try {
        d.await()
    } catch (e: NetworkException) {
        println("Network error: ${e.message}")
    } catch (e: Exception) {
        println("Other error: ${e.message}")
    }
}

Deferred.getCompleted() per un controllo sincrono

Dopo join(), utilizzi getCompleted() per recuperare il risultato in modo sincrono. Genera un'eccezione se il deferred non è riuscito.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val d = async { 42 }
    d.join()  // wait without caring about result
    try {
        val result = d.getCompleted()  // synchronous — no suspend
        println("Result: $result")
    } catch (e: Exception) {
        println("Failed: ${e.message}")
    }
}

Concorrenza strutturata e async

Con la concorrenza strutturata, un deferred async è sempre collegato allo scope padre. Quando lo scope viene annullato, viene annullato anche il deferred: non rimangono attività orfane.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val scope = CoroutineScope(SupervisorJob())
    val d = scope.async {
        delay(1000)
        "result"
    }
    scope.cancel()  // cancels d too
    try { d.await() }
    catch (e: CancellationException) { println("Cancelled as expected") }
}

Buona pratica: attendere sempre

Attenda sempre ogni deferred async, anche se non serve il risultato. In questo modo le eccezioni vengono rese visibili e si prevengono gli errori silenziosi.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val d = async {
            // do background work
            "done"
        }
        // Always await:
        val result = runCatching { d.await() }
        println(result)
    }
}

Verifica rapida

Quando viene resa visibile l'eccezione proveniente da un blocco async { throw ... }?

Riepilogo

async memorizza le eccezioni nel Deferred, che diventano visibili in corrispondenza di await(). In coroutineScope, la propagazione annulla i fratelli. In supervisorScope, ogni await deve gestire il proprio errore. Attenda sempre ogni deferred.

Domande Frequenti

La lezione «Propagazione delle eccezioni con async/await» è gratuita?

Sì — il testo completo di «Propagazione delle eccezioni con async/await» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Kotlin Academy, passa a CoddyKit PRO. Il corso Kotlin Academy include 4 lezioni in totale.

Cosa imparerò in «Propagazione delle eccezioni con async/await»?

Comprenda come si propagano le eccezioni da async e quando usare try-await. Eserciti Kotlin Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Kotlin Academy?

Non è richiesta alcuna esperienza precedente. Kotlin Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Propagazione delle eccezioni con async/await»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Kotlin Academy?

Sì. Ogni lezione Kotlin Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. SupervisorJob e Job: isolamento degli errori
  2. CoroutineExceptionHandler: gestore globale delle eccezioni non catturate
  3. Propagazione delle eccezioni con async/await
  4. Progettare architetture resilienti per le coroutine
← Torna a Kotlin Academy