提问者:小点点

为什么会有withContext等待子协程的完成


with Context状态的留档

使用给定的协程上下文调用指定的挂起块,挂起直到它完成,并返回结果。

但是,实际行为是它也在所有子协程上等待,并且不一定返回块的结果,而是在子协程中传播任何异常。

suspend fun main() {
    try {
        val result = withContext(coroutineContext) {
            launch {
                delay(1000L)
                throw Exception("launched coroutine broke")
            }
            println("done launching")
            42
        }
        println ("result: $result")
    } catch (e: Exception) {
        println("Error: ${e.message}")
    }
}

我希望上面打印结果:42,然后可能打印子协程的未捕获异常。相反,它等待一秒钟,然后打印错误:启动的协程已损坏

因此,实际行为与coroutineScope构建器的行为相匹配。虽然它可能是一个有用的行为,但我认为它与留档相矛盾。留档是否应该更新为类似于coroutineScope的东西?

一旦给定块及其所有子协程完成,此函数就会返回。

此外,这是否意味着我们可以互换使用coroutineScopewith Context(coroutineContext),唯一的区别是样板少一点?


共2个答案

匿名用户

withContext创建一个新作业。这意味着里面启动的所有协程都是这个作业的子协程。它只在作业完成时返回。由于结构化并发,它只在所有子协程也完成时完成。

当任何子作业失败时,父作业将被取消。这也将取消所有其他子作业。由于withContext返回结果,因此引发异常。

CoroutineScope的留档在这方面很有帮助:

每个协程构建器(如启动、异步等)和每个作用域函数(如coroutineScope、withContext等)都将自己的作用域与自己的Job实例一起提供到它运行的内部代码块中。按照惯例,它们都等待其块内的所有协程完成后再完成自己,从而强制执行结构化并发的纪律。

我认为with Context的留档也可以改进。JobCoroutineContext的留档非常有用,因为它们提供了更高级的观点。

此外,这是否意味着我们可以互换使用coroutineScope和withContext(coroutineContext),唯一的区别是样板文件少一点?

是的,它们应该以相同的方式运行。不过,它们适用于不同的用例。

coroutineScope旨在为多个并行协程提供一个范围,如果有失败,所有协程都将被取消。

withContext旨在用于切换给定代码块的上下文(例如Dispatcher)。

这是我最近在kotlin论坛上提出的一个类似的问题。该主题包含一些更多类似的案例和进一步的见解。

匿名用户

withcontext的行为是正确的。可以想象,如果发生异常,下面的代码无论如何都不会执行,除非您在该位置本身而不是在函数级别处理了异常。

当你启动一个协程函数,我们可以做这样的事情,

var job=启动{delay(1000L)抛出异常("启动的协程中断了")}job.调用On完成{异常-

加入

基本上,它将处理异常,不会传播到父协程。或者创建一个CoroutineExceptionHandler并将其传递给启动(CoroutineExceptionHandler)以自己处理它。