Necesito llamar a una función de suspensión dentro de un bloque suspendCoroutine , antes de llamar a continuation.resume() . ¿Cuál es la forma adecuada de hacerlo?
private suspend fun someFunction() = suspendCoroutine { cont -> //... val myResult = mySuspendingFunction() //<--- The IDE says "Suspension functions can be called only within coroutine body" cont.resume(myResult) }No puede llamar a una función de suspend en el bloque suspendCoroutine , porque acepta el bloque sin suspensión como parámetro:
suspend inline fun <T> suspendCoroutine( crossinline block: (Continuation<T>) -> Unit ): T'suspendCoroutine' se usa principalmente cuando tenemos algún código heredado con devoluciones de llamada, por ejemplo:
suspend fun getUser(id: String): User = suspendCoroutine { continuation -> Api.getUser(id) { user -> continuation.resume(user) } } Si la función someFunction() no llama a Api con devoluciones de llamada, entonces debería reconsiderar su enfoque para deshacerse de 'suspendCoroutine':
private suspend fun someFunction() { // ... val myResult = mySuspendingFunction() // ... } Si aún desea usar suspendCoroutine mueva la llamada de mySuspendingFunction fuera del bloque 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" }Es mejor evitar esto y llamar a la función de suspensión antes suspendCoroutine , como han respondido otros. Eso es posible para el caso específico en cuestión.
Sin embargo, eso no es posible si necesita la continuación.
(Lo siguiente es para aquellos que encontraron esta pregunta por este motivo, como @Zordid y yo tenemos. chan.send es un ejemplo de esto).
En cuyo caso, la siguiente es una forma posible, pero propensa a errores , de hacerlo, que no recomiendo:
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 }) } }(Creo que el manejo de errores por sí solo ilustra por qué no es una gran opción, a pesar de otros problemas).
Una forma mejor, más segura y más económica es usar CompletableDeferred si puede.
Si debe pasar en una Continuación, aún es más seguro y probablemente más barato hacerlo:
suspend fun cont2() { val rslt = CompletableDeferred<Unit>() chan.send(foo(Continuation(currentCoroutineContext()) { rslt.completeWith(it) })) rslt.await() }