I was playing around with coroutines and found some very strange behavior. I want to convert some asynchronous requests in my project using suspendCoroutine(). Here's piece of code showing this problem.

In first case, when suspend function is being called in runBlocking coroutine, exception from continuation goes to catch block, and then runBlocking finishes successfully. But in second case, when creating new async coroutine, exception goes through catch block and crashes the whole program.

package com.example.lib

import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

object Test {
    fun runSuccessfulCoroutine() {
        runBlocking {
            try {
                Repository.fail()
            } catch (ex: Throwable) {
                println("Catching ex in runSuccessfulCoroutine(): $ex")
            }
        }
    }

    fun runFailingCoroutine() {
        runBlocking {
            try {
                async { Repository.fail() }.await()
            } catch (ex: Throwable) {
                println("Catching ex in runFailingCoroutine(): $ex")
            }
        }
    }
}

object Repository {
    suspend fun fail(): Int = suspendCoroutine { cont ->
        cont.resumeWithException(RuntimeException("Exception at ${Thread.currentThread().name}"))
    }
}


fun main() {
    Test.runSuccessfulCoroutine()
    println()

    Test.runFailingCoroutine()

    println("We will never get here")
}

That's what is printed on console:

Catching ex in runSuccessfulCoroutine(): java.lang.RuntimeException: Exception at main

Catching ex in runFailingCoroutine(): java.lang.RuntimeException: Exception at main
Exception in thread "main" java.lang.RuntimeException: Exception at main
    at com.example.lib.Repository.fail(MyClass.kt:32)
    at com.example.lib.Test$runFailingCoroutine$1$1.invokeSuspend(MyClass.kt:22)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:236)
    at kotlinx.coroutines.EventLoopBase.processNextEvent(EventLoop.kt:123)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:69)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:45)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:35)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at com.example.lib.Test.runFailingCoroutine(MyClass.kt:20)
    at com.example.lib.MyClassKt.main(MyClass.kt:41)
    at com.example.lib.MyClassKt.main(MyClass.kt)

Process finished with exit code 1

Any ideas why this is happening - is it a bug, or am i using coroutines the wrong way?

Update:

Using coroutineScope { ... } will mitigate problem in runFailingCoroutine()

fun runFailingCoroutine() = runBlocking {
    try {
        coroutineScope { async { fail() }.await()  }
    } catch (ex: Throwable) {
        println("Catching ex in runFailingCoroutine(): $ex")
    }
}

推荐答案

您的第二个示例的行为是正确的,这是 struct 化并发的工作. 因为内async挡路抛出一个异常,所以这个协同 routine 被取消.由于 struct 化并发,父作业(job)也被取消.

看看这个小例子:

val result = coroutineScope {
    async {
        throw IllegalStateException()
    }
    10
}

此块永远不会返回值,即使我们从未请求async结果.内部协同程序被取消,外部作用域也被取消.

如果你不喜欢这种行为,你可以使用supervisorScope.在这种情况下,内部协同路由可能会失败,而不会使外部协同路由失败.

val result = supervisorScope {
    async {
        throw IllegalStateException()
    }
    10
}

In your first example you catch the exception inside of the coroutine block, because of this, the coroutine exits normally.

有关此主题的讨论,请参阅:

Kotlin相关问答推荐

使用另一个对象的列表创建对象

KTOR';S函数`staticResources`在Kotlin本机目标上不可用

Java/Kotlin中类似Rust般的注释编译?

如何从 Period.between() 返回的字符串中提取信息? (Kotlin )

Flow.state In() 未从其来源接收新值

测试协程和线程之间的差异,我在kotlin中使用线程时无法得到OOM错误

从 HashMap 检索时的 NPE,即使 containsKey() 在多线程环境中返回 true

Kotlin 中二叉树的深度

is return inside function definition 也是 kotlin 中的表达式

为什么 Kotlin 扩展运算符在传递原始可变参数时需要 toTypedArray()?

如何从 Firestore 查询中排除元素?

在 Scaffold Jetpack Compose 内的特定屏幕上隐藏顶部和底部导航器

验证和 DDD - kotlin 数据类

用mockk验证属性设置程序吗?

Kotlin的BiMap/2-way hashmap

Kotlin:如何从另一个类访问字段?

Kotlin 警告:Conditional branch result of type ... is implicity cast of Any?

IllegalStateException:function = , count = 3, index = 3

可以在函数参数中使用解构吗?

Kotlin反射不可用