这段代码:
fun main() {
runBlocking {
try {
val deferred = async { throw Exception() }
deferred.await()
} catch (e: Exception) {
println("Caught $e")
}
}
println("Completed")
}
这会导致以下输出结果:
Caught java.lang.Exception
Exception in thread "main" java.lang.Exception
at org.mtopol.TestKt$main$1$deferred$1.invokeSuspend(test.kt:11)
...
这种行为对我来说没有意义。异常已被捕获和处理,但仍然作为未处理的异常逃逸到顶层。
这种行为是否有文档记录并且是期望的?它违反了我对异常处理应该工作的所有直觉。
我从Kotlin论坛的一个帖子中改编了这个问题。
Kotlin文档建议使用supervisorScope
,如果我们不想在一个协程失败时取消所有协程。因此我可以这样写
fun main() {
runBlocking {
supervisorScope {
try {
launch {
delay(1000)
println("Done after delay")
}
val job = launch {
throw Exception()
}
job.join()
} catch (e: Exception) {
println("Caught $e")
}
}
}
println("Completed")
}
现在的输出是{{output}}。
Exception in thread "main" java.lang.Exception
at org.mtopol.TestKt$main$2$1$job$1.invokeSuspend(test.kt:16)
...
at org.mtopol.TestKt.main(test.kt:8)
...
Done after delay
Completed
这不是我想要的行为。在此,一个已启动的协程由于未处理的异常而失败,从而使其他协程的工作失效,但它们仍会继续运行。
我认为合理的行为是,在协程以无法预料(即未处理)的方式失败时,应该传播取消。从await中捕获异常意味着没有全局错误,只有作为业务逻辑的一部分处理的局部异常。
Caught java.lang.Exception Completed
... 请问你使用的是哪个版本? - Rolandasync
中处理异常可能不适合使用情况。也许我想要在此期间获取一些其他的异步结果,作为能够处理异常的先决条件。async
调用也可能是一个不受我控制的库的一部分。 - Marko Topolnik