在上一篇文章中有说过,协程中的异常最初是由下向上抛的,那么我可不可以在 中间层协程中捕获异常呢,下面看一下案例
kotlin
@JvmStatic
fun main(array: Array<String>) {
runBlocking {
var error=CoroutineExceptionHandler { _, throwable ->
println("Tsm Error:${throwable}")
}
var scope= CoroutineScope(SupervisorJob()+ Dispatchers.IO)
scope.launch {
launch {
launch {
launch(error) {
launch {
throw NullPointerException("我错了")
}
}
}
}
}
delay(5*1000)
}
}
在这里创建了一个新的 CoroutineScope ,指定io线程 与 阻止子协程异常而向导致其他兄弟协程异常退出,最终在最内层的子协程中跑了一个异常,并在倒数第二层的协程中传入了 CoroutineExceptionHandler 想要拦截异常
看一下结果
php
Exception in thread "DefaultDispatcher-worker-2 @coroutine#6" java.lang.NullPointerException: 我错了
at com.tsm.opencv.Tsm$main$1$1$1$1$1$1.invokeSuspend(Tsm.kt:23)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:100)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [CoroutineId(2), "coroutine#2":StandaloneCoroutine{Cancelling}@1397bbe2, Dispatchers.IO]
Process finished with exit code 0
发现这个异常还是引起了崩溃,说明这个异常最终只能抛给顶级父协程来处理,中间协程拦截不了
我们再来看一下 async 的表现
typescript
@JvmStatic
fun main(array: Array<String>) {
runBlocking {
var error=CoroutineExceptionHandler { _, throwable ->
println("Tsm Error:${throwable}")
}
var scope= CoroutineScope(SupervisorJob()+ Dispatchers.IO)
scope.async{
async {
async {
async(error) {
async {
throw NullPointerException("我错了")
}
}
}
}
}
delay(5*1000)
}
}
结果:
Process finished with exit code 0
虽然抛出了异常但是程序没有接受到这个异常,我们使用await 接受一下结果看一下
swift
Exception in thread "main" java.lang.NullPointerException: 我错了
at com.tsm.opencv.Tsm$main$1$1$1$1$1$1.invokeSuspend(Tsm.kt:44)
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:46)
at com.tsm.opencv.Tsm$main$1.invokeSuspend(Tsm.kt:49)
Caused by: java.lang.NullPointerException: 我错了
at com.tsm.opencv.Tsm$main$1$1$1$1$1$1.invokeSuspend(Tsm.kt:44)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:100)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
Process finished with exit code 1
可以看到我们使用await后异常就出现了,这说明 async 虽然也收到了这个异常,但是只有在await后,才会获取异常或者结果从而将他抛出
下面将异常交给顶级父协程来处理看一下结果
先看一下launch 的表现
kotlin
@JvmStatic
fun main(array: Array<String>) {
runBlocking {
var error=CoroutineExceptionHandler { _, throwable ->
println("Tsm Error:${throwable}")
}
var scope= CoroutineScope(SupervisorJob()+ Dispatchers.IO)
scope.launch(error) {
launch {
launch {
launch{
launch {
throw NullPointerException("我错了")
}
}
}
}
}
delay(5*1000)
}
}
结果:
Tsm Error:java.lang.NullPointerException: 我错了
Process finished with exit code 0
可以看到异常确实被捕获了
再来看看 async
less
@JvmStatic
fun main(array: Array<String>) {
runBlocking {
var error = CoroutineExceptionHandler { _, throwable ->
println("Tsm Error:${throwable}")
}
var scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
var job=scope.async(error) {
throw NullPointerException("我错了")
}
job.await()
delay(1 * 1000)
}
}
结果:
Exception in thread "main" java.lang.NullPointerException: 我错了
at com.tsm.opencv.Tsm$main$1$job$1.invokeSuspend(Tsm.kt:42)
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:46)
at com.tsm.opencv.Tsm$main$1.invokeSuspend(Tsm.kt:44)
Caused by: java.lang.NullPointerException: 我错了
at com.tsm.opencv.Tsm$main$1$job$1.invokeSuspend(Tsm.kt:42)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:100)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
Process finished with exit code 1
可以发现在使用async 的过程中即使使用了 CoroutineExceptionHandler 还是抛出了异常,这个是为什么呢, 原因与我们在上面讲的 只有await 的时候才可以获取 这个异常是一样的, 是因为 async 方法只是创建一个协程,但是异常只有调用了await 才能获取,那么在实际开发中该如何处理这种情况呢
1: 在 async 中使用 try catch 捕获异常,保险起见 CoroutineExceptionHandler 还是要在顶级父协程加上的 方式如下
kotlin
@JvmStatic
fun main(array: Array<String>) {
runBlocking {
var error = CoroutineExceptionHandler { _, throwable ->
println("Tsm Error:${throwable}")
}
var scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
var job=scope.async(error) {
try {
throw NullPointerException("我错了")
} catch (e: Exception) {
}
}
job.await()
delay(1 * 1000)
}
}
2: 为了保险起见在 async 中同样使用 CoroutineExceptionHandler,但是在 await 出try catch
kotlin
@JvmStatic
fun main(array: Array<String>) {
runBlocking {
var error = CoroutineExceptionHandler { _, throwable ->
println("Tsm Error:${throwable}")
}
var scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
var job=scope.async(error) {
throw NullPointerException("我错了")
}
job.runCatching { await() }.getOrElse {
}
delay(1 * 1000)
}
}
3: 使用launch 作为顶级父协程 + CoroutineExceptionHandler
kotlin
@JvmStatic
fun main(array: Array<String>) {
runBlocking {
var error = CoroutineExceptionHandler { _, throwable ->
println("Tsm Error:${throwable}")
}
var scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch(error) {
var job = async {
throw NullPointerException("我错了")
}
job.await()
}
delay(1 * 1000)
}
}
可以看到使用第三种方法更为的优雅但是具体选择就看个人的喜好了,