Search code examples
androidkotlinkotlinx.coroutines

How can I catch an exception in Kotlin coroutine when I am awaiting it in another function?


Sorry for the vague title, couldn't come up with something better.

So I read this article and wanted to do the same. problem is that I can't do try { promise... } catch (e) { } cause the error gets swallowed. I can catch the error where I await it, but I don't want that.

and my code looks like this:

typealias Promise<T> = Deferred<T>

fun <T, R> Promise<T>.then(handler: (T) -> R): Promise<R> = GlobalScope.async(Dispatchers.Main) {
    // using try/catch here works but I don't want it here.
    val result = this@then.await()
    handler.invoke(result)
}

object PromiseUtil {
    fun <T> promisify(block: suspend CoroutineScope.() -> T): Promise<T> = GlobalScope.async { block.invoke(this) }
}

// somewhere in my UI testing it.
try {
    PromiseUtil.promisify { throw Exception("some exp") }
        .then { Log.d("SOME_TAG", "Unreachable code.") }
} catch (e: Exception) {
    Log.d("ERROR_TAG", "It should catch the error here but it doesn't.")
}

And I read this and this one too, but I want to somehow catch errors in the UI code, and don't want to use runBlocking { ... }.

Thanks.


Solution

  • The exception is never caught because it's never propagated by the async call. That happens when await() is called.

    See coroutine exception handling.

    Your code should be:

    // somewhere in my UI testing it.
    try {
        PromiseUtil.promisify { throw Exception("some exp") }
            .then { Log.d("SOME_TAG", "Unreachable code.") }.await() // <--- added await() call
    } catch (e: Exception) {
        Log.d("ERROR_TAG", "It should catch the error here but it doesn't.")
    }
    

    But this won't compile as await() is a suspending function. Therefore, it should be more like:

    // somewhere in my UI testing it.
    GlobalScope.launch(CoroutineExceptionHandler { coroutineContext, throwable ->
                Log.d("ERROR_TAG", "It will catch error here")
                throwable.printStackTrace()
            }) {
       PromiseUtil.promisify { throw Exception("some exp") }
            .then { Log.d("SOME_TAG", "Unreachable code.") }.await()
    }