coroutineScope , supervisorScope 和 launch ,async 的混合异常处理

特性

launchasync

  • launch :在协程内部马上抛出异常,未捕获的异常不会被保存, join 中也不会保存和传递异常,可是会接受由父协程传递过来的 JobCancellationException
  • async :不在协程内部马上抛出异常,而将未捕获的异常保存在生成的 Deferred 中,最后抛出时已经回到父协程了。

coroutineScopesupervisorScope

  • coroutineScope 内部的异常会向上传播,子协程未捕获的异常会向上传递给父协程,任何一个子协程异常退出,会致使总体的退出。
  • supervisorScope 内部的异常不会向上传播,一个子协程异常退出,不会影响父协程和兄弟协程的运行。

示例

try {
    coroutineScope {
        try {
            val child1 = launch {
                log("Child1 is started")
                delay(100)
                throw ArithmeticException("Child1 error")
            }
			child1.join()
        } catch (e: Exception) {
            log("Child1: $e")
        }
        try {
            val child2 = launch {
                log("Child2 is started")
                delay(1000)
                log("Child2 is finished")
            }
            child2.join()
        } catch (e: Exception) {
            log("Child2: $e")
        }
    }
} catch (e: Exception) {
    log("Parent: $e")
}
复制代码

切换外层的 coroutineScopesupervisorScope ,再对 child1 切换使用 launchasync ,最后获得下表html

条件 child1 的 catch child 2 的 catch parent 的 catch
coroutineScope + launch JobCancellationException Child1 error
coroutineScope + launch + join JobCancellationException JobCancellationException Child1 error
coroutineScope + async JobCancellationException Child1 error
coroutineScope + async + await Child1 error JobCancellationException Child1 error
supervisorScope + launch JobCancellationException Child1 error
supervisorScope + launch + join Child1 error JobCancellationException Child1 error
supervisorScope + async
supervisorScope + async + await Child1 error

总结

launch 不管其父协程做用域是 coroutineScope 仍是 supervisorScope ,都会致使整个父协程做用域范围内的协程的退出。而且由于 join 不会保存和传播异常,即便 launch 自己在外部 try/catch 里执行了 joincatch 中捕获的也只是由父协程发出的取消。async

async 由于是将未捕获的异常保存在生成的 Deferred 中,在父协程做用域是 coroutineScope 时,虽然一样由于将异常抛给父协程致使整个父协程做用域范围内的协程的退出。可是在外部 try/catch 里执行 await 时, catch 也可以捕获发生在 async 内部的异常。在父协程做用域是 supervisorScope 是,该 async 的崩溃不会影响兄弟协程和父协程其余代码的运行。spa

一般状况下,使用经常使用的 coroutineScope + launch ,将异常捕获写在 launch 中就能够了。.net

但在同时运行多个子协程时,有两种状况下,使用 supervisorScope + async 更为便利。code

  1. 不能在协程内部调用 try/catch ,且一个协程的崩溃不能致使全部协程的退出
  2. 能够在协程内部调用 try/catch ,可是对全部协程抛出的异常处理逻辑是相同的,且一个协程的崩溃应致使全部协程的退出

参考文章 :协程

Kotlin协程中的launch/join和async/await之间有什么区别htm

异常处理作用域

Kotlin 协程(4) - 异常处理篇get

相关文章
相关标签/搜索