英文:
Why is join() required to allow the catch part of a try catch to execute for an uncaught exception in Kotlin coroutines?
问题
为什么只有caughtExceptionTest1打印"caught Exception"?我理解协程将被取消,因为throwException()在launch {}范围内未被捕获,但为什么只有在我们join()抛出异常的Job时catch块才会执行?
英文:
I have this code
fun caughtExceptionTest1() {
runBlocking {
try {
launch { throwException() }.join()
} catch (e: Exception) {
// This prints
print("caught Exception")
}
}
}
fun caughtExceptionTest2() {
runBlocking {
try {
launch { throwException() }
} catch (e: Exception) {
// This does not print
print("caught Exception")
}
}
}
Why does only caughtExceptionTest1 print "caught Exception"? I understand that the coroutines will be cancelled because throwException() is uncaught in the launch {} scope but why does the catch block only execute if we join() the Job that throws the exception?
答案1
得分: 1
launch
函数被设计成非阻塞的,意味着它启动一个协程但不等待其完成。因此,在caughtExceptionTest2
中,try-catch在协程有机会抛出异常之前就已经完成,所以你的异常不会在那里被捕获。
与此同时,caughtExceptionTest1
中的join
会挂起当前的协程,直到它所调用的协程完成,这意味着在异常发生时try-catch块仍然处于活动状态,允许它捕获异常。
阅读更多:https://kotlinlang.org/docs/exception-handling.html
英文:
The launch
function is designed to be non-blocking(!), meaning it starts a coroutine but doesn't wait for its completion. So in caughtExceptionTest2
the try-catch finishes before the coroutine has a chance to throw an exception, and your exception doesn't get caught there
Meanwhile the join
in caughtExceptionTest1
suspends the current coroutine until the one it's invoked on completes, meaning the try-catch block is still active when the exception occurs, allowing it to catch the exception
Read more: https://kotlinlang.org/docs/exception-handling.html
答案2
得分: 1
只有翻译的部分:
请注意,您的第一个测试函数也没有捕获throwException()
抛出的异常。它仍然会逃逸出该函数并被抛到caughtExceptionTest1()
的调用方。您只捕获了由join()
调用创建并抛出的CancellationException。
您两个函数之间唯一的区别是,在第一个函数中,通过调用join()
生成了一个额外的异常,即CancellationException,然后立即捕获并记录它。
launch
旨在将协程内部引发的未捕获异常传递给其父协程,因此它不会像您期望的那样将其抛给紧邻的代码。父协程然后处理它,行为取决于父协程的类型。在这种情况下,父协程是runBlocking
协程,它将重新抛出异常给调用runBlocking
的代码。
英文:
Note that your first test function is not catching the exception thrown by throwException()
either. It will still escape the function and be thrown to the caller of caughtExceptionTest1()
. You are only catching the CancellationException created by and thrown by your join()
call.
The only difference between your two functions is that in the first one, you generate an extra Exception, the CancellationException, by calling join()
and then immediately catch it and log it.
launch
is designed to pass uncaught exceptions internal to the coroutine to its parent coroutine, so it doesn't throw it to the immediately surrounding code like you expected. The parent coroutine then handles it, and the behavior depends on what type of coroutine the parent is. In this case, the parent is the runBlocking
coroutine, and it will rethrow the exception to the code that called runBlocking
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论