在协程范围内引发异常时,协程范围可重用吗? [英] When you throw an exception in a coroutine scope, is the coroutine scope reusable?

查看:105
本文介绍了在协程范围内引发异常时,协程范围可重用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've been having problems figuring out error handling with coroutines that I've narrowed down to this unit test with the following steps:

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

  1. I create a coroutine scope, with any dispatcher.
  2. I throw an exception anywhere within this scope in an async block (or even in a nested async block).
  3. I call await on the returned deferred value and handle the exception.
  4. This is all fine. However, when I try to use the same coroutine scope to launch a new coroutine, this always completes exceptionally with the same exception.

这是测试:

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,但仍然得到相同的结果.
    • Do I need to create a new coroutineScope?
    • Can I never throw exceptions if I want to keep using the same coroutineScope?
    • Should I return Either<Result, Exception>?
    • I've tried using CoroutineExceptionHandler but I still get the same results.

    请注意,我正在使用Kotlin 1.3

    Note I'm using Kotlin 1.3

    推荐答案

    在范围中启动协程(使用asynclaunch)时,默认情况下协程失败会立即取消该范围取消所有其他孩子.这种设计避免了晃动和丢失异常.

    When you start a coroutine in a scope (using either async or launch), then a failure of a coroutine by default cancels this scope to promptly cancel all the other children. This design avoid dangling and lost exceptions.

    这里的一般建议是:

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

    • Don't use async/await unless you really need concurrency. When you design your code with suspending functions there is no much need to use async and await.

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

    If you do need concurrent execution, then follow the pattern:

    coroutineScope { 
        val d1 = async { doOne() }
        val d2 = async { doTwo() }
        ...
        // retrieve and process results
        process(d1.await(), d2.await(), .... )
    }
    

  • 如果您需要处理并发操作的失败,请在coroutineScope { ... }周围放置try { ... } catch { ... },以捕获任何并发执行的操作中的失败.

    If you need to handle a failure of a concurrent operation, then put try { ... } catch { ... } around coroutineScope { ... } to catch a failure in any of the concurrently executing operations.

    • There are additional advanced mechanisms (like SupervisorJob) that allow fine-grained exception handling. You can read more in the documentation https://kotlinlang.org/docs/reference/coroutines/exception-handling.html

    这篇关于在协程范围内引发异常时,协程范围可重用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆