为什么在Kotlin协程中启动吞咽异常? [英] Why does launch swallow exceptions in kotlin coroutines?

查看:118
本文介绍了为什么在Kotlin协程中启动吞咽异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下测试以Process finished with exit code 0成功.请注意,此测试确实会将异常打印到日志中,但不会使测试失败(这是我想要的行为).

The following test succeeds with Process finished with exit code 0. Note, this test does print the exception to the logs, but does not fail the test (which is the behavior I want).

@Test
fun why_does_this_test_pass() {
    val job = launch(Unconfined) {
        throw IllegalStateException("why does this exception not fail the test?")
    }

    // because of `Unconfined` dispatcher, exception is thrown before test function completes
}

如预期的那样,此测试失败,并显示Process finished with exit code 255

As expected, this test fails with Process finished with exit code 255

@Test
fun as_expected_this_test_fails() {
    throw IllegalStateException("this exception fails the test")
}

为什么这些测试的行为方式不一样?

Why do these tests not behave the same way?

推荐答案

将您的测试与以下不使用任何协程而是启动新线程的测试进行比较:

Compare your test with the following one that does not use any coroutines, but starts a new thread instead:

@Test
fun why_does_this_test_pass() {
    val job = thread { // <-- NOTE: Changed here
        throw IllegalStateException("why does this exception not fail the test?")
    }
    // NOTE: No need for runBlocking any more
    job.join() // ensures exception is thrown before test function completes
}

这是怎么回事?就像使用launch进行的测试一样,如果运行该测试,则通过,但是异常会在控制台上打印.

What happens here? Just like the test with launch, this test passes if you run it, but the exception gets printed on the console.

因此,使用launch启动新的协程非常类似于使用thread启动新的线程.如果失败,则错误由thread中未捕获的异常处理程序处理,由launchCoroutineExceptionHandler处理(在文档中查看).启动时的异常不会被吞噬,而是由协程异常处理程序进行处理.

So, using launch to start a new coroutine is very much like using thread to start a new thread. If it fails, the error gets handled by uncaught exception handler in thread and by CoroutineExceptionHandler (see it in the docs) by launch. Exceptions in launch are not swallowed, but are handled by the coroutine exception handler.

如果希望异常传播到测试,则应在代码中将launch替换为async,并将join替换为await.另请参阅以下问题:什么Kotlin协程中启动/联接和异步/等待之间的区别是什么

If you want exception to propagate to the test, you shall replace launch with async and replace join with await in your code. See also this question: What is the difference between launch/join and async/await in Kotlin coroutines

更新:Kotlin协程最近引入了结构化并发"概念,以避免此类异常丢失.此问题中的代码不再编译.要进行编译,您必须明确说出GlobalScope.launch(如我确认可以放开我的异常,这是我的签名"),或将测试包装到runBlocking { ... }中,在这种情况下,异常不是丢失的.

UPDATE: Kotlin coroutines had recently introduced the concept of "Structured Concurrency" to avoid this kind of exception loss. The code in this question does not compile anymore. To compile it, you'd have to either explicitly say GlobalScope.launch (as in "I confirm that it Ok to loose my exceptions, here is my signature") or wrap the test into runBlocking { ... }, in which case exception is not lost.

这篇关于为什么在Kotlin协程中启动吞咽异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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