Careeroza
Exceptions and Completion
catch, onStart, onCompletion
catch { } handles exceptions from upstream operators (not from the collector below it). It can emit a fallback value. onCompletion runs at the end, with the cause if it failed.
Example
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.io.IOException
fun liveScores(): Flow<String> = flow {
emit("IND 120/2")
emit("IND 145/3")
throw IOException("socket closed")
}
fun main() = runBlocking {
liveScores()
.onStart { emit("Loading...") }
.onCompletion { cause -> println("onCompletion, error = ${cause?.message}") }
.catch { e ->
println("catch: ${e.message}")
emit("Score unavailable")
}
.collect { println("UI: $it") }
}Output
UI: Loading... UI: IND 120/2 UI: IND 145/3 onCompletion, error = socket closed catch: socket closed UI: Score unavailable
retry and retryWhen
Example
Retry the whole upstream when a recoverable error happens.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.io.IOException
var attempts = 0
fun prices(): Flow<Int> = flow {
attempts++
println("Connecting, attempt $attempts")
if (attempts < 3) throw IOException("connection reset")
emit(2941)
emit(2945)
}
fun main() = runBlocking {
prices()
.retryWhen { cause, attempt ->
val retry = cause is IOException && attempt < 3
if (retry) delay(100L * (attempt + 1))
retry
}
.catch { println("Giving up: ${it.message}") }
.collect { println("Price: $it") }
}Output
Connecting, attempt 1 Connecting, attempt 2 Connecting, attempt 3 Price: 2941 Price: 2945
Note: Exceptions thrown inside collect { } are not caught by an upstream catch. Move the logic into onEach { } before catch, then call collect() with no block.