在 suspendCoroutine 块中调用挂起函数的适当方法是什么?



在调用continuation.resume()之前,我需要在suspendCoroutine块内调用一个挂起函数。 这样做的适当方法是什么?

private suspend fun someFunction() = suspendCoroutine { cont ->
//...
val myResult = mySuspendingFunction() //<--- The IDE says "Suspension functions can be called only within coroutine body"
cont.resume(myResult)
}

您不能在suspendCoroutine块中调用suspend函数,因为它接受非挂起块作为参数:

suspend inline fun <T> suspendCoroutine(
crossinline block: (Continuation<T>) -> Unit
): T

"suspendCoroutine"主要用于我们有一些带有回调的遗留代码,例如:

suspend fun getUser(id: String): User = suspendCoroutine { continuation ->
Api.getUser(id) { user ->
continuation.resume(user)
}
}

如果函数someFunction()没有使用回调调用 Api,那么您应该重新考虑摆脱"挂起协程"的方法:

private suspend fun someFunction() {
// ...
val myResult = mySuspendingFunction()
// ...
}

如果您仍想使用suspendCoroutinemySuspendingFunction的调用移出suspendCoroutine块:

private suspend fun someFunction(): String {
val myResult = mySuspendingFunction()
return suspendCoroutine { cont ->
//...
cont.resume(myResult)
}
}
suspend fun mySuspendingFunction(): String {
delay(1000) // simulate request
return "result"
}

最好避免这种情况,并在suspendCoroutine之前调用挂起函数,正如其他人所回答的那样。对于所讨论的特定情况,这是可能的。

但是,如果您需要继续,则无法做到这一点。

(以下是那些因为这个原因而发现这个问题的人,就像@Zordid和我一样。chan.send就是一个例子。

在这种情况下,我不建议使用以下可能但容易出错的方法:

suspend fun cont1() {
//btw. for correct implementation, this should most likely be at least suspendCancellableCoroutine
suspendCoroutine<Unit> { uCont ->
val x = suspend { chan.send(foo(uCont)) }
x.startCoroutine(Continuation(uCont.context) {
if (it.isFailure)
uCont.resumeWith(it)
// else resumed by whatever reads from chan
})
}
}

(我认为仅错误处理就说明了为什么尽管存在其他问题,但它不是一个很好的选择。

一个更好,更安全,更便宜的方法是尽可能使用CompletableDeferred

如果您必须传入延续,它仍然更安全,并且可能更便宜:

suspend fun cont2() {
val rslt = CompletableDeferred<Unit>()
chan.send(foo(Continuation(currentCoroutineContext()) {
rslt.completeWith(it)
}))
rslt.await()
}

相关内容

最新更新