我正在密集地使用协程来创建服务,但我面临着一个将异常从我的服务内部转换到外部的问题.以下是我想在没有Coroutine的情况下做的综合(here is the full example in a playground:

class MyService {
    
    fun myBigComputation(type: MyServiceType) {
        try {
            for (i in (0..10_000)) {
                mySubComputation(type, i)
            }
        } catch (e: LowLevelException) {
            throw HighLevelException(type, e)
        }
    }
    
    private fun mySubComputation(type: MyServiceType, i: Int) {        
        ...
        // something bad happend 
        throw LowLevelException(type)            
    }
    
    ...
}

你可以看到我正在把LowLevelException改成HighLevelException.在日常生活中,最好的方法是什么?

That is not working as LowLevelException fails all the structure until the supervisorScope Playground

suspend fun main() = coroutineScope {
    
    val service = MyService()
    
    supervisorScope {
        for (type in MyService.MyServiceType.values()) {
            launch(CoroutineExceptionHandler { _, e -> 
                if (e is HighLevelException) {
                    println("ERROR: ${e.message}")    
                }
            }) {
                service.myBigComputation(type)
            }
        }
    }
}

class MyService {
    
    suspend fun myBigComputation(type: MyServiceType) = coroutineScope {
        launch(CoroutineExceptionHandler { _, e -> 
            if (e is LowLevelException) {
                throw HighLevelException(type, e)
            }
        }) {
            for (i in (0..10)) {
                mySubComputation(type, i)
            }
        }
    }
    
    private fun mySubComputation(type: MyServiceType, i: Int) {
        if (i < 5 || type != MyServiceType.type1) {
            print(".")
        } else {
            // something bad happend
            println("something bad happened but exception kill all")
            throw LowLevelException(type)            
        }
    }
    
    class LowLevelException(val type: MyServiceType): Exception()
    
    enum class MyServiceType {
        type1,
        type2,
        type3
    }
}

class HighLevelException(val type: MyService.MyServiceType, e: Exception): Exception("Exception for type $type", e)

I did that, but I'm pretty sure that there is a better way, no ? Playground

suspend fun main() = coroutineScope {
    
    val service = MyService()
    
    supervisorScope {
        for (type in MyService.MyServiceType.values()) {
            launch(CoroutineExceptionHandler { _, e -> 
                if (e is HighLevelException) {
                    println("ERROR: ${e.message}")    
                }
            }) {
                service.myBigComputation(type)
            }
        }
    }
}

class MyService {
    
    suspend fun myBigComputation(type: MyServiceType) = supervisorScope {
        launch(CoroutineExceptionHandler { _, e -> 
            if (e is LowLevelException) {
                throw HighLevelException(type, e)
            }
        }) {
            for (i in (0..10)) {
                mySubComputation(type, i)
            }
        }
    }
    
    //...
}

推荐答案

您试图使用CoroutineExceptionHandler来执行此操作,这会使问题过于复杂.CoroutineExceptionHandler用于未捕获的异常.当它被调用时,您的协程已经抛出并失败了.它旨在成为一种非常高级的行为,如果您熟悉的话,就像Java中的uncaughtExceptionHandler一样.

而且,您还将CoroutineExceptionHandler附加到子协程.在child协程上设置CoroutineExceptionHandler没有任何效果,因为它只用于处理未捕获的异常,而子协程会将异常传播给它们的父级.解释见第二段第in the documentation here段.

因此,您的代码应该看起来更像:

suspend fun main() {
    val handler = CoroutineExceptionHandler { _, e -> 
        if (e is HighLevelException) {
            println("ERROR: ${e.message}")    
        }
    }
    runBlocking(handler) {
        val service = MyService()
        supervisorScope {
            for (type in MyService.MyServiceType.values()) {
                launch {
                    service.myBigComputation(type)
                }
            }
        }
    }
}

class MyService {
    
    suspend fun myBigComputation(type: MyServiceType) {
        try {
            for (i in (0..10)) {
                mySubComputation(type, i)
            }
        } catch(e: Exception) {
            throw if (e is LowLevelException) {
                HighLevelException(type, e)
            } else {
                e
            }
        }
    }

    //...

Kotlin相关问答推荐

如何在使用Kotlin Coroutines时检测和记录何时出现背压

如何在Jetpack Compose中从领域查询中读取数据?

在Kotlin中求n个ClosedRange实例相交的最常用方法是什么?

Kotlin异步不并行运行任务

编译后的JavaFX应用程序立即以静默方式崩溃

在 detekt 配置文件中找不到某些属性

ActivityResultContracts TakePicture 结果总是返回 false

修改器的属性是什么,我需要更改以使角变圆且宽度更小?喷气背包组合

Kotlin 函数有 2 个参数用于对 Map 或 List 进行排序

正则表达式 FindAll 不打印结果 Kotlin

Kotlin 中的 Java Scanner 相当于什么?

在构造函数中仅注入某些参数

无法在 kotlin android 中以编程方式禁用 EditText

requireNotNull vs sure !! 操作符

如果 Maybe 完成,则从其他来源将 Maybe 转换为 Single

我们如何在Java注释声明中引用Kotlin常量?

在kotlin中初始化类变量的正确位置是什么

将协程更新到 1.2.0 后构建失败:META-INF/atomicfu.kotlin_module

Kotlin 错误:public function exposes its 'public/*package*/' return type argument

如何在 spring-boot Web 客户端中发送请求正文?