给定范围内的安全异步

Mat*_*ans 5 kotlin kotlin-coroutines

我想在给定的parent中启动一个带有挂起函数的异步协程CoroutineScope,以生成一个Deferred可以从该范围内的任何协程使用的a。

我希望如果父级的作业被取消,它的作业也被取消,但是如果挂起函数抛出异常,我需要在结果中捕获该异常,Deferred而不取消父级范围中的同级作业。

我这样做的方式效果很好,但我想知道是否有比这更简单、更符合规范的方法:

fun <T> callIt(scope: CoroutineScope, block: suspend () -> T) : Deferred<T> {
    val result = CompletableDeferred<T>()
    scope.launch {
        try {
            result.complete(block())
        } catch (e: Throwable) {
            result.completeExceptionally(e)
        }
    }
    return result
}
Run Code Online (Sandbox Code Playgroud)

我喜欢对挂起异常的处理block显然是我想要的,但我不太高兴构建async一个launch

不起作用的事情:

  • 带有异常处理程序的异步作业。 async捕获其异常,但作业仍然失败并取消其父作业。正如 @Rene 评论的: 的文档async说:“如果未能强制执行结构化并发范例,它会取消父作业(或外部范围)。”。

Ren*_*ene 3

您无需CompletableDeferred. 您需要创建一个作业SupervisorJob,并让其中的作业CoroutineScope成为新作业的父作业。如果范围被取消,async协程也会被取消。但 -coroutine 内的任何异常都async不会取消父作用域。

由于存在一个开放问题https://github.com/Kotlin/kotlinx.coroutines/issues/1578,我们需要显式完成 SupervisorJob。

fun <T> callIt(scope: CoroutineScope, block: suspend () -> T): Deferred<T> {
    val supervisorJob = SupervisorJob(scope.coroutineContext[Job])
    val deferred = scope.async(supervisorJob) {
        block()
    }
    deferred.invokeOnCompletion {
        supervisorJob.complete()
    }
    return deferred
}
Run Code Online (Sandbox Code Playgroud)