gpt4 book ai didi

kotlin - runBlocking 中的 deferred.await() 抛出的异常即使在被捕获后也被视为未处理

转载 作者:IT老高 更新时间:2023-10-28 13:42:23 25 4
gpt4 key购买 nike

这段代码:

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 forum 上的一个帖子中改编了这个问题.


如果我们不想在协程失败时取消所有协程,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")
}

现在是输出

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

这又不是我想要的行为。这里一个 launched 协程因未处理的异常而失败,使其他协程的工作无效,但它们不会中断。

我认为合理的行为是在协程以不可预见(即未处理)的方式失败时传播取消。从 await 捕获异常意味着没有任何全局错误,只是作为业务逻辑的一部分处理的本地化异常。

最佳答案

在研究了 Kotlin 引入这种行为的原因后,我发现如果不以这种方式传播异常,那么编写能够及时取消的行为良好的代码会很复杂。例如:

runBlocking {
val deferredA = async {
Thread.sleep(10_000)
println("Done after delay")
1
}
val deferredB = async<Int> { throw Exception() }
println(deferredA.await() + deferredB.await())
}

因为 a 是我们碰巧等待的第一个结果,所以这段代码会持续运行 10 秒,然后导致错误并且没有完成任何有用的工作。在大多数情况下,我们希望在一个组件出现故障时立即取消所有内容。我们可以这样做:

val (a, b) = awaitAll(deferredA, deferredB)
println(a + b)

这段代码不太优雅:我们被迫在同一个地方等待所有结果,并且我们失去了类型安全性,因为 awaitAll 返回所有参数的公共(public)父类(super class)型列表。如果我们有一些

suspend fun suspendFun(): Int {
delay(10_000)
return 2
}

我们想写

val c = suspendFun()
val (a, b) = awaitAll(deferredA, deferredB)
println(a + b + c)

我们被剥夺了在 suspendFun 完成之前退出的机会。我们可以这样解决:

val deferredC = async { suspendFun() }
val (a, b, c) = awaitAll(deferredA, deferredB, deferredC)
println(a + b + c)

但这很脆弱,因为您必须注意确保对每个可暂停的调用都执行此操作。这也违反了 Kotlin 的“默认顺序”学说

总结:当前的设计虽然起初违反直觉,但作为一种实用的解决方案确实有意义。它还加强了不使用 async-await 的规则,除非您正在对任务进行并行分解。

关于kotlin - runBlocking 中的 deferred.await() 抛出的异常即使在被捕获后也被视为未处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53222045/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com