将异常从异步传播到 CoroutineExceptionHandler
Propagate exception from async to CoroutineExceptionHandler
我有一个 async
协程,它可能会抛出异常。如何将异常传播到我的 CoroutineExceptionHandler
?在 await
附近 try/catch
我能够捕获异常,但我似乎无法将处理传播到处理程序,无论上下文如何:
val handler = CoroutineExceptionHandler { _, e -> e.printStackTrace() }
val context = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + handler
val scope = CoroutineScope(context)
val async = scope.async {
throw Exception("Test exception")
}
runBlocking {
try {
async.await()
} catch (e: Exception) {
log.error("Exception thrown", e)
}
delay(1_000)
}
我试过从捕获中重新抛出异常,或者用以下一些结构包装它:
catch (e: Exception) {
throw e
// withContext(context) { throw e }
// scope.launch { throw e }
// supervisorScope { throw e }
}
可悲的是,其中 none 将其传播给了处理程序。我能以某种方式利用 CoroutineExceptionHandler
和 async
协程吗?
不,你不能。
async
returns 一个 Deferred,这就像一个未来。异常处理程序不适用,因为如果有异常,它是 Deferred 的 return 值。
来自文档:
In addition to that, async builder always catches all exceptions and represents them in the resulting Deferred object, so its CoroutineExceptionHandler has no effect either.
来源:https://kotlinlang.org/docs/exception-handling.html#coroutineexceptionhandler
有几种方法可以让它发挥作用。首先,正如 async documentation 中所述,它会在失败时取消父作业。这就是为什么 scope.launch { throw e }
什么都不做,范围在那个时候被取消,协程甚至没有启动。
然后,如 supervised coroutines documentation 中所述,要在受监督的上下文中处理未捕获的异常,您应该将处理程序安装到子上下文中。所以例如以下代码会将异常传递给您的处理程序
runBlocking {
supervisorScope {
launch(handler){
async.await()
}
}
}
另一种方法是将异常处理程序安装到协程的根范围内,例如像那样
runBlocking {
GlobalScope.launch(handler) {
async.await()
}.join()
}
这里我使用GlobalScope
进行演示,但它可以是任何活动的(未取消的)范围。
coroutine exception handling doc 和 运行 中有很多示例我建议查看。
我有一个 async
协程,它可能会抛出异常。如何将异常传播到我的 CoroutineExceptionHandler
?在 await
附近 try/catch
我能够捕获异常,但我似乎无法将处理传播到处理程序,无论上下文如何:
val handler = CoroutineExceptionHandler { _, e -> e.printStackTrace() }
val context = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + handler
val scope = CoroutineScope(context)
val async = scope.async {
throw Exception("Test exception")
}
runBlocking {
try {
async.await()
} catch (e: Exception) {
log.error("Exception thrown", e)
}
delay(1_000)
}
我试过从捕获中重新抛出异常,或者用以下一些结构包装它:
catch (e: Exception) {
throw e
// withContext(context) { throw e }
// scope.launch { throw e }
// supervisorScope { throw e }
}
可悲的是,其中 none 将其传播给了处理程序。我能以某种方式利用 CoroutineExceptionHandler
和 async
协程吗?
不,你不能。
async
returns 一个 Deferred,这就像一个未来。异常处理程序不适用,因为如果有异常,它是 Deferred 的 return 值。
来自文档:
In addition to that, async builder always catches all exceptions and represents them in the resulting Deferred object, so its CoroutineExceptionHandler has no effect either.
来源:https://kotlinlang.org/docs/exception-handling.html#coroutineexceptionhandler
有几种方法可以让它发挥作用。首先,正如 async documentation 中所述,它会在失败时取消父作业。这就是为什么 scope.launch { throw e }
什么都不做,范围在那个时候被取消,协程甚至没有启动。
然后,如 supervised coroutines documentation 中所述,要在受监督的上下文中处理未捕获的异常,您应该将处理程序安装到子上下文中。所以例如以下代码会将异常传递给您的处理程序
runBlocking {
supervisorScope {
launch(handler){
async.await()
}
}
}
另一种方法是将异常处理程序安装到协程的根范围内,例如像那样
runBlocking {
GlobalScope.launch(handler) {
async.await()
}.join()
}
这里我使用GlobalScope
进行演示,但它可以是任何活动的(未取消的)范围。
coroutine exception handling doc 和 运行 中有很多示例我建议查看。