asynchronous - 在 Kotlin 异步协程中捕获异常并停止传播
问题描述
我想捕获从异步协程抛出的异常。下面的代码演示了一个问题:
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
try {
println(failedConcurrentSum())
} catch (e: ArithmeticException) {
println("Computation failed with ArithmeticException")
}
}
suspend fun failedConcurrentSum() = coroutineScope {
try {
val one = async {
try {
delay(1000L)
42
} finally {
println("First child was cancelled")
}
}
val two = async<Int> {
println("Second child throws an exception")
throw ArithmeticException()
}
one.await() + two.await()
} catch (e: ArithmeticException) {
println("Using a default value...")
0
}
}
这打印:
Second child throws an exception
First child was cancelled
Computation failed with ArithmeticException
内部try-catch
不failedConcurrentSum
处理val two
. 我可以说服自己,这是由于“结构化并发”。
但是,这并不能解释为什么将async
' 包装在 acoroutineScope
中会捕获异常:
suspend fun failedConcurrentSum() = coroutineScope {
try {
val one = coroutineScope {
async {
try {
delay(1000L)
42
} finally {
println("First child was cancelled")
}
}
}
val two = coroutineScope {
async<Int> {
println("Second child throws an exception")
throw ArithmeticException()
}
}
one.await() + two.await()
} catch (e: ArithmeticException) {
println("Using a default value...")
0
}
}
这打印:
First child was cancelled
Second child throws an exception
Using a default value...
0
为什么后者捕获异常而第一个没有?
解决方案
coroutineScope
is just a function, it sets up its scope internally and from the outside it always completes like a regular function, not messing with any outer scopes. This is because it doesn't leak any concurrent coroutines started within its scope. You can always reliably catch and handle exceptions thrown by coroutineScope
.
async
, on the other hand, completes immediately after launching the coroutine, therefore you have two concurrent coroutines: one running the async
code and another calling the corresponding await
. Since the async
one is also the child of the one calling await
, its failure cancels the parent before the parent's await
call completes.
The try-catch inside the
failedConcurrentSum
doesn't handle the exception thrown byval two
.
It actually does, if it gets the chance. But since the try-catch block is in a coroutine that runs concurrently to the one completing val two
's Deferred
, it just doesn't get the chance to do so before being cancelled due to the failure of the child coroutine.
推荐阅读
- java - 这怎么可能?Text 和 RelativeLayout 的颜色相同,并且会发生这种情况
- pandas - 具有多个类别的箱线图和小提琴图
- c# - WCF 数据服务显示未处理异常的错误
- amazon-web-services - 为什么会提示“我们计算的请求签名与您提供的签名不匹配”。对于 GET 但不是 PUT 对于 OpenSearch?
- javascript - 具有和不具有代理支持的不同代码的 Npm 模块
- discord.py - Discord.py - AttributeError: 'NoneType' 对象没有属性 'connect'
- java - 带有 CDATA 的 VDT xml 解析器 getContentFragment
- ios - 理解转义闭包 Swift
- javascript - 使用 Gmail Oauth 仅限制特定电子邮件登录
- javascript - Prisma:如何从生成的类型中排除属性