Kotlin协程无法处理异常 [英] Kotlin coroutine can't handle exception

查看:686
本文介绍了Kotlin协程无法处理异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在和协程一起玩耍,发现一些非常奇怪的行为.我想使用suspendCoroutine()在我的项目中转换一些异步请求.这是显示此问题的代码.

I was playing around with coroutines and found some very strange behavior. I want to convert some asynchronous requests in my project using suspendCoroutine(). Here's piece of code showing this problem.

在第一种情况下,当在runBlocking协程中调用suspend函数时,来自延续的异常进入catch块,然后runBlocking成功完成.但是在第二种情况下,当创建新的async协程时,异常会通过catch块并使整个程序崩溃.

In first case, when suspend function is being called in runBlocking coroutine, exception from continuation goes to catch block, and then runBlocking finishes successfully. But in second case, when creating new async coroutine, exception goes through catch block and crashes the whole program.

package com.example.lib

import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

object Test {
    fun runSuccessfulCoroutine() {
        runBlocking {
            try {
                Repository.fail()
            } catch (ex: Throwable) {
                println("Catching ex in runSuccessfulCoroutine(): $ex")
            }
        }
    }

    fun runFailingCoroutine() {
        runBlocking {
            try {
                async { Repository.fail() }.await()
            } catch (ex: Throwable) {
                println("Catching ex in runFailingCoroutine(): $ex")
            }
        }
    }
}

object Repository {
    suspend fun fail(): Int = suspendCoroutine { cont ->
        cont.resumeWithException(RuntimeException("Exception at ${Thread.currentThread().name}"))
    }
}


fun main() {
    Test.runSuccessfulCoroutine()
    println()

    Test.runFailingCoroutine()

    println("We will never get here")
}

这就是控制台上显示的内容:

That's what is printed on console:

Catching ex in runSuccessfulCoroutine(): java.lang.RuntimeException: Exception at main

Catching ex in runFailingCoroutine(): java.lang.RuntimeException: Exception at main
Exception in thread "main" java.lang.RuntimeException: Exception at main
    at com.example.lib.Repository.fail(MyClass.kt:32)
    at com.example.lib.Test$runFailingCoroutine$1$1.invokeSuspend(MyClass.kt:22)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:236)
    at kotlinx.coroutines.EventLoopBase.processNextEvent(EventLoop.kt:123)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:69)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:45)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:35)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at com.example.lib.Test.runFailingCoroutine(MyClass.kt:20)
    at com.example.lib.MyClassKt.main(MyClass.kt:41)
    at com.example.lib.MyClassKt.main(MyClass.kt)

Process finished with exit code 1

关于发生这种情况的任何想法-是bug,还是我以错误的方式使用协同程序?

Any ideas why this is happening - is it a bug, or am i using coroutines the wrong way?

更新:

使用coroutineScope { ... }将减轻runFailingCoroutine()

fun runFailingCoroutine() = runBlocking {
    try {
        coroutineScope { async { fail() }.await()  }
    } catch (ex: Throwable) {
        println("Catching ex in runFailingCoroutine(): $ex")
    }
}

推荐答案

第二个示例的行为是正确的,这是结构化并发的工作. 由于内部async块引发异常,因此该协程被取消.由于结构性并发,父作业也被取消.

The behavior of your second example is correct, this is the work of structured concurrency. Because the inner async block throws an exception, this coroutine is cancelled. Due to structured concurrency the parent job is cancelled as well.

看看这个小例子:

val result = coroutineScope {
    async {
        throw IllegalStateException()
    }
    10
}

即使我们从不请求async结果,该块也将永远不会返回值.内部协程被取消,外部范围也被取消.

This block will never return a value, even if we never request the async result. The inner coroutine is cancelled and the outer scope is cancelled as well.

如果您不喜欢这种行为,可以使用supervisorScope.在这种情况下,内部协程可能会失败,而外部协程不会失败.

If you don't like this behavior you can use the supervisorScope. In this case the inner coroutine can fail without failing the outer coroutine.

val result = supervisorScope {
    async {
        throw IllegalStateException()
    }
    10
}

在第一个示例中,您在协程块内捕获了异常,因此,协程正常退出.

In your first example you catch the exception inside of the coroutine block, because of this, the coroutine exits normally.

有关此主题的讨论,请参见:

For discussion of this topic see:

  • https://github.com/Kotlin/kotlinx.coroutines/issues/552
  • https://github.com/Kotlin/kotlinx.coroutines/issues/763

这篇关于Kotlin协程无法处理异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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