catch e onCompletion: gestione degli errori in Flow
Intercetti le eccezioni upstream e reagisca agli eventi di completamento del flow.
catch e onCompletion: gestione degli errori in Flow è una lezione Kotlin Academy gratuita su CoddyKit. Questa è la lezione 2 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.
Nozioni di base sulle eccezioni nei Flow
Le eccezioni in un flusso lo terminano. Se non vengono gestite, l'eccezione si propaga al raccoglitore e il flusso termina.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
try {
flow {
emit(1)
throw RuntimeException("stream error")
emit(2)
}.collect { println(it) }
} catch (e: Exception) {
println("Caught: ${e.message}")
}
}Operatore catch
catch gestisce le eccezioni del flusso upstream e può emettere valori di fallback oppure rilanciare l'eccezione. Non intercetta le eccezioni generate dal raccoglitore stesso.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
flow {
emit(1)
throw RuntimeException("oops")
}.catch { e ->
println("Caught: ${e.message}")
emit(-1) // fallback value
}.collect { println(it) } // 1, then -1
}catch gestisce solo le eccezioni upstream
catch intercetta solo le eccezioni generate dal costruttore del flusso e dagli operatori upstream, non quelle generate dalla lambda di collect.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
try {
flowOf(1, 2, 3)
.catch { e -> println("upstream: ${e.message}") }
.collect {
if (it == 2) throw RuntimeException("collector error") // not caught by catch
println(it)
}
} catch (e: Exception) {
println("Collector exception: ${e.message}")
}
}Rilancio in catch
Può esaminare l'eccezione e rilanciare selettivamente quelle che non può gestire.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
try {
flow {
emit(1)
throw IllegalStateException("state error")
}.catch { e ->
if (e is RuntimeException) { emit(-1); return@catch }
throw e // rethrow unknown exceptions
}.collect { println(it) }
} catch (e: Exception) {
println("Rethrown: ${e.message}")
}
}Operatore onCompletion
onCompletion viene eseguito quando il flusso termina, normalmente o con un'eccezione. Il suo parametro cause non è null in caso di errore.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
flowOf(1, 2, 3)
.onCompletion { cause ->
if (cause == null) println("Completed normally")
else println("Failed: ${cause.message}")
}
.collect { println(it) }
}onCompletion in caso di errore
onCompletion viene eseguito anche quando si verifica un'eccezione nel flusso upstream. Riceve l'eccezione come cause, ma non la sopprime: l'eccezione continua a propagarsi.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
try {
flow {
emit(1)
throw RuntimeException("error")
}.onCompletion { cause ->
println("onCompletion cause=${cause?.message}")
}.collect { println(it) }
} catch (e: Exception) {
println("outer catch: ${e.message}")
}
}Combinazione di catch e onCompletion
Utilizzi catch per recuperare dall'errore ed emettere valori di fallback, quindi onCompletion per registrare informazioni o rilasciare risorse indipendentemente dall'esito.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
flow {
emit("ok")
throw RuntimeException("network error")
}.catch { e ->
emit("fallback") // recover
}.onCompletion {
println("Stream done — release resources")
}.collect { println(it) }
}Operatore retry
retry(n) { ... } si sottoscrive nuovamente al flusso upstream in caso di eccezione, fino a n volte.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
var attempt = 0
fun unstableFlow() = flow {
attempt++
if (attempt < 3) throw RuntimeException("attempt $attempt failed")
emit("success on attempt $attempt")
}
fun main() = runBlocking {
unstableFlow()
.retry(3) { e -> println("retrying: ${e.message}"); true }
.collect { println(it) }
}retryWhen per la logica personalizzata
retryWhen fornisce l'eccezione e il numero di tentativi, consentendo di aggiungere un backoff o di filtrare le eccezioni che attivano un nuovo tentativo.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
var n = 0
flow {
if (n++ < 2) throw IOException("IO error")
emit("done")
}.retryWhen { cause, attempt ->
cause is IOException && attempt < 3
}.collect { println(it) }
}Operatore onStart
onStart viene eseguito prima della prima emissione: è utile per mostrare stati di caricamento o inizializzare le risorse prima dell'arrivo dei dati.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
flowOf("data")
.onStart { emit("Loading...") }
.onCompletion { emit("Done") }
.collect { println(it) }
}Gestione degli errori nelle chiamate API reali
Avvolga i flussi del repository con catch per associare le eccezioni agli errori di dominio e con onCompletion per nascondere gli indicatori di caricamento.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
sealed class State { object Loading : State(); data class Data(val v: String) : State(); data class Error(val msg: String) : State() }
fun apiFlow(): Flow<String> = flow { delay(50); emit("response") }
fun uiFlow(): Flow<State> = apiFlow()
.map { State.Data(it) as State }
.onStart { emit(State.Loading) }
.catch { e -> emit(State.Error(e.message ?: "unknown")) }
fun main() = runBlocking { uiFlow().collect { println(it) } }Controllo rapido
Qual è la limitazione principale dell'operatore catch?
Riepilogo
catch gestisce le eccezioni upstream e può emettere valori di fallback. onCompletion viene sempre eseguito (in caso di completamento normale o di errore) per le operazioni di pulizia o la registrazione dei log. Li combini con retry/retryWhen per ottenere pipeline di flussi resilienti.
Domande Frequenti
La lezione «catch e onCompletion: gestione degli errori in Flow» è gratuita?
Sì — il testo completo di «catch e onCompletion: gestione degli errori in Flow» è 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 «catch e onCompletion: gestione degli errori in Flow»?
Intercetti le eccezioni upstream e reagisca agli eventi di completamento del flow. 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 2 di 4.
Quanto tempo richiede la lezione «catch e onCompletion: gestione degli errori in Flow»?
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
- Operatori di Flow: map, filter, transform e take
- catch e onCompletion: gestione degli errori in Flow
- combine e zip: unire più Flow
- flowOn e buffer: contesto e backpressure