在协程范围内引发异常时,协程范围可重用吗?

时间:2018-11-29 23:30:32

标签: kotlin error-handling kotlin-coroutines

我一直难以确定协程的错误处理,我已经按照以下步骤缩小了单元测试的范围:

  1. 我与任何调度程序一起创建一个协程范围。
  2. 我在异步块(甚至嵌套的异步块)中在此范围内的任何地方抛出异常。
  3. 我对返回的延期值进行等待,并处理异常。
  4. 这很好。但是,当我尝试使用相同的协程范围来启动新的协程时,总是会例外地完成,但有相同的例外。

    这是测试:

    fun `when you throw an exception in a coroutine scope, is the coroutine scope dead?`() {
        val parentJob = Job()
        val coroutineScope = CoroutineScope(parentJob + Dispatchers.Default)
    
        val deferredResult = coroutineScope.async { throw IllegalStateException() }
    
        runBlocking {
            try {
                deferredResult.await()
            } catch (e: IllegalStateException) {
                println("We caught the exception. Good.")
            }
    
            try {
                coroutineScope.async { println("we can still use the scope") }.await()
            } catch (e: IllegalStateException) {
                println("Why is this same exception still being thrown?")
            }
    
        }
    
    }
    

这是测试的输出:

We caught the exception. Good.
Why is this same exception still being thrown?
  • 为什么会这样?

    • 我的理解是,您可以正常处理异常并使用协程从异常中恢复。
  • 我应该如何处理异常?

    • 我需要创建一个新的coroutineScope吗?
    • 如果我想继续使用相同的coroutineScope,可以永远不会抛出异常吗?
    • 我应该退回Either<Result, Exception>吗?
    • 我尝试使用CoroutineExceptionHandler,但仍然得到相同的结果。

请注意,我正在使用Kotlin 1.3

1 个答案:

答案 0 :(得分:5)

当您在合并范围中启动协程时(使用STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), ) STATIC_URL = '/static/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media/') MEDIA_URL = '/media/' async),默认情况下,协程失败会取消该范围,以立即取消所有其他子级。这种设计避免了晃动和丢失异常。

这里的一般建议是:

  • 除非确实需要并发,否则不要使用launch / async。使用挂起函数设计代码时,不需要使用awaitasync

  • 如果确实需要并发执行,请遵循以下模式:

    await

如果您需要处理并发操作的失败,请在coroutineScope { val d1 = async { doOne() } val d2 = async { doTwo() } ... // retrieve and process results process(d1.await(), d2.await(), .... ) } 周围加上try { ... } catch { ... }来捕获任何并发执行的操作中的失败。