当你在协程范围内抛出异常时,协程范围是否可重用?
When you throw an exception in a coroutine scope, is the coroutine scope reusable?
我在使用协程找出错误处理时遇到了问题,我已通过以下步骤将其缩小到此单元测试:
- 我用任何调度程序创建了一个协程作用域。
- 我在异步块(甚至嵌套异步块)中此范围内的任何位置抛出异常。
- 我在 returned 延迟值上调用 await 并处理异常。
这一切都很好。但是,当我尝试使用相同的协程范围来启动新的协程时,这总是异常完成并出现相同的异常。
这是测试:
fun `when you throw an exception in a coroutine scope, is the coroutine scope dead?`() {
val parentJob = Job()
val coroutineScope = CoroutineScope(parentJob + Dispatchers.Default)
val deferredResult = coroutineScope.async { throw IllegalStateException() }
runBlocking {
try {
deferredResult.await()
} catch (e: IllegalStateException) {
println("We caught the exception. Good.")
}
try {
coroutineScope.async { println("we can still use the scope") }.await()
} catch (e: IllegalStateException) {
println("Why is this same exception still being thrown?")
}
}
}
这是测试的输出:
We caught the exception. Good.
Why is this same exception still being thrown?
为什么会这样?
- 我的理解是您可以正常处理异常并使用协程从中恢复。
异常应该怎么处理?
- 我需要创建一个新的coroutineScope吗?
- 如果我想继续使用相同的协程作用域,我可以永远不抛出异常吗?
- 我应该return
Either<Result, Exception>
吗?
- 我试过使用 CoroutineExceptionHandler,但我仍然得到相同的结果。
注意我使用的是 Kotlin 1.3
当您在一个范围内启动协程时(使用 async
或 launch
),默认情况下协程失败会取消该范围以立即取消所有其他子级。这种设计避免了悬挂和丢失的异常。
这里的一般建议是:
除非你真的需要并发,否则不要使用async
/await
。当您设计带有挂起函数的代码时,没有太多需要使用 async
和 await
。
如果确实需要并发执行,那么遵循模式:
coroutineScope {
val d1 = async { doOne() }
val d2 = async { doTwo() }
...
// retrieve and process results
process(d1.await(), d2.await(), .... )
}
如果您需要处理并发操作的失败,那么将 try { ... } catch { ... }
放在 coroutineScope { ... }
周围以捕获任何并发执行操作的失败。
- 还有其他高级机制(如
SupervisorJob
)允许细粒度异常处理。您可以在文档 https://kotlinlang.org/docs/reference/coroutines/exception-handling.html 中阅读更多内容
我在使用协程找出错误处理时遇到了问题,我已通过以下步骤将其缩小到此单元测试:
- 我用任何调度程序创建了一个协程作用域。
- 我在异步块(甚至嵌套异步块)中此范围内的任何位置抛出异常。
- 我在 returned 延迟值上调用 await 并处理异常。
这一切都很好。但是,当我尝试使用相同的协程范围来启动新的协程时,这总是异常完成并出现相同的异常。
这是测试:
fun `when you throw an exception in a coroutine scope, is the coroutine scope dead?`() { val parentJob = Job() val coroutineScope = CoroutineScope(parentJob + Dispatchers.Default) val deferredResult = coroutineScope.async { throw IllegalStateException() } runBlocking { try { deferredResult.await() } catch (e: IllegalStateException) { println("We caught the exception. Good.") } try { coroutineScope.async { println("we can still use the scope") }.await() } catch (e: IllegalStateException) { println("Why is this same exception still being thrown?") } } }
这是测试的输出:
We caught the exception. Good.
Why is this same exception still being thrown?
为什么会这样?
- 我的理解是您可以正常处理异常并使用协程从中恢复。
异常应该怎么处理?
- 我需要创建一个新的coroutineScope吗?
- 如果我想继续使用相同的协程作用域,我可以永远不抛出异常吗?
- 我应该return
Either<Result, Exception>
吗? - 我试过使用 CoroutineExceptionHandler,但我仍然得到相同的结果。
注意我使用的是 Kotlin 1.3
当您在一个范围内启动协程时(使用 async
或 launch
),默认情况下协程失败会取消该范围以立即取消所有其他子级。这种设计避免了悬挂和丢失的异常。
这里的一般建议是:
除非你真的需要并发,否则不要使用
async
/await
。当您设计带有挂起函数的代码时,没有太多需要使用async
和await
。如果确实需要并发执行,那么遵循模式:
coroutineScope { val d1 = async { doOne() } val d2 = async { doTwo() } ... // retrieve and process results process(d1.await(), d2.await(), .... ) }
如果您需要处理并发操作的失败,那么将 try { ... } catch { ... }
放在 coroutineScope { ... }
周围以捕获任何并发执行操作的失败。
- 还有其他高级机制(如
SupervisorJob
)允许细粒度异常处理。您可以在文档 https://kotlinlang.org/docs/reference/coroutines/exception-handling.html 中阅读更多内容