I am trying to understand why the exception is passed up the hierarchy in case of async
when not surrounded by coroutineScope
? Why is there no such need of coroutineScope
when handling exception in launch
?
fun main() {
runBlocking {
this.launch {// case 1
try {
throw java.lang.IndexOutOfBoundsException()
} catch (e: Exception) {
println("Caught IndexOutOfBoundsException")
}
}
try {
coroutineScope {
val deferred = async {// case 2
throw ArithmeticException()
}
deferred.await()
}
} catch (e: ArithmeticException) {
println("Caught ArithmeticException")
}
try {
val deferred = async {//case 3
throw ArithmeticException()
}
deferred.await()
} catch (e: Exception) {
println("Caught ArithmeticException but passed to root as well")
}
}
}
My current output is following:
Caught IndexOutOfBoundsException
Caught ArithmeticException
Caught ArithmeticException but passed to root as well
Exception in thread "main" java.lang.ArithmeticException
at TryItYourself3Kt$main$1$deferred$1.invokeSuspend(TryItYourself3.kt:26)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:277)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:95)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:69)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:48)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at TryItYourself3Kt.main(TryItYourself3.kt:5)
at TryItYourself3Kt.main(TryItYourself3.kt)
Process finished with exit code 1
I'm not sure if I got your question right:
Case 1: we catch the exception before it got to the coroutines machinery, so coroutines don't even know there was an exception.
Case 2: async
is a child of coroutineScope
. Both of them fail, but both are enclosed in try...catch
, so we catch the exception that comes from coroutineScope
.
Case 3: async
is a child of runBlocking
, so both of them fail. We catch the failure from deferred.await()
, but runBlocking
itself still fails.