Search code examples
kotlin-coroutinesstructured-concurrency

Kotlin coroutines launch vs async exception handling


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

Solution

  • 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.