Reputation: 915
I am running a coroutine in a SupervisorJob
with a try/catch block surrounding only the await calls. The exception from the async block gets caught by the try/catch, but it still gets propagated and the app crashes.
This is what I have:
CoroutineScope(Dispatchers.IO + SupervisorJob()).launch {
val a = async {
delay(500)
throw Exception("excep a")
2
}
val b = async {
delay(500)
3
}
try {
println(a.await() + b.await())
} catch (e: Exception) {
println("exception: ${e.message}")
}
}
This is what I get (note that "excep a" gets caught):
exception: excep a
Exception in thread "DefaultDispatcher-worker-3 @coroutine#1" java.lang.Exception: excep a
at com.example.app.AuthTest$co2$1$a$1.invokeSuspend(AuthTest.kt:314)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Upvotes: 4
Views: 2380
Reputation: 2318
SupervisorJob
has launch
as Top coroutine and both async/await
are nested coroutines.
Nested async/await
always propagated up via Job hierarchy even if you use try/catch.
launch
as Top coroutine require CoroutineExceptionHandler.
Alternatively you can:
async
and try/catch
in supervisorScope
async
and try/catch
in coroutineScope
inside try/catch
. But in this case second async
will be cancelled after exception is thrownUpvotes: 1
Reputation: 91
You are creating a parent coroutine using launch builder having IO dispatcher and SupervisorJob. Then you are creating two children coroutines using async biulder. But remember the child coroutines do not inherit the Job from its parent. Each child coroutine creates their own Job. So to avoid app crashing and exception apply the same supervisorjob explicitly to each child as shown below.
val supervisorJob = SupervisorJob()
CoroutineScope(Dispatchers.IO).launch {
val a = async(supervisorJob) {
delay(500)
throw Exception("excep a")
2
}
val b = async(supervisorJob) {
delay(500)
3
}
try {
println(a.await() + b.await())
} catch (e: Exception) {
println("exception: ${e.message}")
}
}
Thank you.
Upvotes: -1
Reputation: 951
The exception handling mechanism of supervisorJob and Job is not the same. For supervisory tasks, the transmission of exceptions can only be from the parent scope to the child scope, and the propagation direction of the exception is one-way. Therefore, it is necessary to handle exceptions by itself for monitoring the scope of opening the coroutine.
CoroutineScope(Dispatchers.IO + SupervisorJob()).launch(CoroutineExceptionHandler { _, throwable ->
println("$throwable")
}) {
val a = async {
delay(500)
throw Exception("excep a")
2
}
val b = async {
delay(500)
3
}
println(a.await() + b.await())
}
This exception will be handled in the top-level Job
Upvotes: 3