Kotlin中协程作用域与普通挂起函数的try/catch行为差异原因
协程中try/catch行为差异的核心原因
本质区别:调用栈与协程独立性
普通挂起函数的try/catch和包裹launch的try/catch,核心差异在于异常抛出的执行流是否和try/catch在同一个协程调用栈:
1. 普通挂起函数的try/catch为什么能捕获异常?
挂起函数是在当前协程的执行流内直接执行的,抛出的异常会沿着当前协程的调用栈向上传递,外层的try/catch自然能捕获到。
示例代码:
suspend fun doAction() { try { throw Error("Test Exception") } catch (e: Error) { println("Printed1") // 正常触发 } } // 调用场景 runBlocking { doAction() println("Printed2") // 正常执行,因为异常已被捕获 }
这里doAction的执行完全属于当前协程的一部分,异常不会逃逸到协程上下文,所以try/catch生效。
2. 包裹launch的try/catch为什么无效?
launch的作用是创建一个全新的独立子协程,这个子协程拥有自己的调用栈和执行流,和外层try/catch所在的协程是完全分离的。外层的try/catch只能捕获当前协程执行流中的异常,根本管不到子协程内部抛出的异常。
更关键的是:默认协程上下文(非Supervisor)中,子协程的异常会自动传播到父协程,父协程被取消后会进一步终止整个Scope下的所有协程,导致后续代码无法执行。
示例代码:
runBlocking { try { // 这里的launch创建了独立子协程 launch { throw Error("Test Exception") } } catch (e: Error) { println("永远不会执行到这里") } println("Printed2?不会,因为父协程已被子协程的异常终止") }
正确捕获launch协程异常的两种方式
方式一:将try/catch放在launch内部的代码块中
直接在子协程的执行流内捕获异常,确保异常不会逃逸到协程上下文:
runBlocking { launch { try { throw Error("Test Exception") } catch (e: Error) { println("捕获到子协程异常") } } println("Printed2 正常执行") }
方式二:使用supervisorScope隔离异常传播
supervisorScope会创建一个异常隔离的上下文,子协程的异常只会终止自身,不会传播到父协程或其他子协程:
runBlocking { supervisorScope { launch { throw Error("Test Exception") // 仅当前子协程终止 } println("Printed2 正常执行") } }
内容的提问来源于stack exchange,提问作者Benjamin Jalon
相关产品推荐
相关产品推荐

