给出以下代码段:
摘要[1]
val job = SupervisorJob()
val scope = CoroutineScope(Dispatchers.IO + job)
scope.launch {
try {
scope.async {
throw RuntimeException("Oops!")
}.await()
} catch(e: Exception) {
// Handle exception
}
}
和摘录[2]
val job = SupervisorJob()
val scope = CoroutineScope(Dispatchers.IO + job)
scope.launch {
try {
async {
throw RuntimeException("Oops!")
}.await()
} catch(e: Exception) {
// Handle exception
}
}
第一个起作用,第二个片段崩溃。一般的解释是,在第一种情况下,async
从SupervisorJob
继承了scope
,而在第二种情况下,则没有。
我的问题是,如果async
是CoroutineScope
的扩展函数,为什么在第二种情况下(崩溃)它不会以相同的方式继承SupervisorJob
? / p>
答案 0 :(得分:2)
launch
创建一个新的Job,并将其传播到该块接收的CoroutineScope
中。在第二个片段中,async
作业是launch
作业的子项,而launch
作业在其子项失败时被取消。
在第一个代码段中,async
工作是您创建的SupervisorJob
的孩子,当它的孩子失败时,该工作不会被取消。在这种情况下,launch
工作没有孩子。它只是捕获异常并完成。
答案 1 :(得分:1)
在第一个代码段中,由于您显式覆盖了launch
构建器建立的范围,因此它与其中的async
块之间没有父子关系。
这是编写第一个代码段的等效方法:
val deferred = scope.async {
throw RuntimeException()
}
scope.launch {
try {
deferred.await()
} catch(e: Exception) {
}
}
async
协程失败,但发生异常。它的父级是SupervisorJob
,它将忽略失败。 launch
协程调用Deferred.await()
,这将引发异常。您捕获了异常,但没有任何失败。
您的第二个代码段可以按如下方式重写:
scope.launch {
val innerScope = this
innerScope.async {
throw RuntimeException()
}
}
在这里您可以清楚地看到async
块继承的范围。它不是带有SupervisorJob
的顶层目录,而是launch
创建的内部层目录。当async
协程失败时,父作业会通过先取消所有其他子项(在这种情况下为无),然后取消自身来对它做出反应。 deferred.await()
语句在这里没有区别,这就是为什么我删除了它。 launch
协程将自动等待所有子协程完成。