简体   繁体   English

Kotlin 协程中的异步:捕获到异常并仍在传播?

[英]Kotlin async in coroutine: exception caught and still propagated?

I am running a coroutine in a SupervisorJob with a try/catch block surrounding only the await calls.我在SupervisorJob中运行一个协程,其中一个 try/catch 块仅围绕 await 调用。 The exception from the async block gets caught by the try/catch, but it still gets propagated and the app crashes.来自异步块的异常被 try/catch 捕获,但它仍然被传播并且应用程序崩溃。

This is what I have:这就是我所拥有的:

CoroutineScope(Dispatchers.IO + SupervisorJob()).launch {
            val a = async {
                delay(500)
                throw Exception("excep a")
                2
            }
            val b = async {
                delay(500)
                3
            }
            try {
                println(a.await() + b.await())
            } catch (e: Exception) {
                println("exception: ${e.message}")
            }
        }

This is what I get (note that "excep a" gets caught):这是我得到的(注意“excep a”被捕获):

exception: excep a
Exception in thread "DefaultDispatcher-worker-3 @coroutine#1" java.lang.Exception: excep a
    at com.example.app.AuthTest$co2$1$a$1.invokeSuspend(AuthTest.kt:314)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

The exception handling mechanism of supervisorJob and Job is not the same. supervisorJob 和 Job 的异常处理机制是不一样的。 For supervisory tasks, the transmission of exceptions can only be from the parent scope to the child scope, and the propagation direction of the exception is one-way.对于supervisor任务,异常的传输只能是从父scope到子scope,异常的传播方向是单向的。 Therefore, it is necessary to handle exceptions by itself for monitoring the scope of opening the coroutine.因此需要自行处理异常,以监控scope开启协程。

    CoroutineScope(Dispatchers.IO + SupervisorJob()).launch(CoroutineExceptionHandler { _, throwable ->
        println("$throwable")
    }) {
        val a = async {
            delay(500)
            throw Exception("excep a")
            2
        }
        val b = async {
            delay(500)
            3
        }
        println(a.await() + b.await())
    }

This exception will be handled in the top-level Job此异常将在顶层 Job 中处理

You are creating a parent coroutine using launch builder having IO dispatcher and SupervisorJob .您正在使用具有IO调度程序和SupervisorJob启动构建器创建父协程。 Then you are creating two children coroutines using async biulder.然后,您将使用async biulder 创建两个子协程。 But remember the child coroutines do not inherit the Job from its parent.但请记住,子协程不会从其父协程继承 Job。 Each child coroutine creates their own Job.每个子协程创建自己的 Job。 So to avoid app crashing and exception apply the same supervisorjob explicitly to each child as shown below.因此,为避免应用程序崩溃和异常,将相同的 supervisorjob 明确应用于每个孩子,如下所示。

 val supervisorJob = SupervisorJob()
    CoroutineScope(Dispatchers.IO).launch {
        val a = async(supervisorJob) {
            delay(500)
            throw Exception("excep a")
            2
        }
        val b = async(supervisorJob) {
            delay(500)
            3
        }
        try {
            println(a.await() + b.await())
        } catch (e: Exception) {
            println("exception: ${e.message}")
        }
    }

Thank you.谢谢你。

SupervisorJob has launch as Top coroutine and both async/await are nested coroutines. SupervisorJob已作为 Top 协程launch ,并且async/await都是嵌套协程。

Nested async/await always propagated up via Job hierarchy even if you use try/catch.嵌套的async/await始终通过作业层次结构向上传播,即使您使用 try/catch。

launch as Top coroutine require CoroutineExceptionHandler.作为 Top 协程launch需要 CoroutineExceptionHandler。

Alternatively you can:或者你可以:

  1. wrap both async and try/catch in supervisorScopesupervisorScope中包装asynctry/catch
  2. wrap both async and try/catch in coroutineScope inside try/catch .asynctry/catch包装在try/catch /catch 内的coroutineScope中。 But in this case second async will be cancelled after exception is thrown但在这种情况下,第二个async将在抛出异常后被取消

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM