kotlin 协程上下文中的重新引发异常

v1uwarro  于 2023-03-09  发布在  Kotlin
关注(0)|答案(1)|浏览(151)

我正在大量使用协程来创建服务,但是我面临着一个关于将异常从服务内部转换到外部的问题。下面是我想在没有协程的情况下做的事情的综合(下面是操场上的完整示例:

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。在协程上下文中,最好的方法是什么?

不工作,因为LowLevelException使所有结构失败,直到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)
我是这么做的,但我很确定有更好的方法,不是吗?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)
            }
        }
    }
    
    //...
}
kqlmhetl

kqlmhetl1#

你试图使用CoroutineExceptionHandler来处理这个问题,这会使它变得过于复杂。CoroutineExceptionHandler是用于未捕获的异常的。当它被调用时,你的协程已经抛出并失败了。它被设计成一个非常高级的行为,有点像Java中的uncaughtExceptionHandler,如果你熟悉的话。
但是,您也将CoroutineExceptionHandler附加到子协程。在 child 协程上设置CoroutineExceptionHandler没有任何效果,因为它只用于处理未捕获的异常,并且子协程将异常传播给其父协程。请参阅此处文档的第二段以了解解释。
因此,您的代码应该看起来更像:

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
            }
        }
    }

    //...

相关问题