I need to call a suspending function inside a suspendCoroutine
block, before I call continuation.resume()
.
What is the appropriate way of doing that?
private suspend fun someFunction() = suspendCoroutine { cont ->
//...
val myResult = mySuspendingFunction() //<--- The IDE says "Suspension functions can be called only within coroutine body"
cont.resume(myResult)
}
The definition of suspend function: Suspend function is a function that could be started, paused, and resume.
Suspend functions should be main-safe, meaning they're safe to call from the main thread. If a class is doing long-running blocking operations in a coroutine, it's in charge of moving the execution off the main thread using withContext .
Hunting to know BLOCKING vs SUSPENDINGA process is blocked when there is some external reason that it can not be restarted, e.g., an I/O device is unavailable, or a semaphore file is locked. A process is suspended means that the OS has stopped executing it, but that could just be for time-slicing (multitasking).
A coroutine is an instance of suspendable computation. It may suspend its execution in one thread and resume in another one. delay is a special suspending function. It suspends the coroutine for a specific time.
You can't call a suspend
function in suspendCoroutine
block, because it accepts non suspend block as parameter:
suspend inline fun <T> suspendCoroutine(
crossinline block: (Continuation<T>) -> Unit
): T
'suspendCoroutine' mainly used when we have some legacy code with callbacks, e.g.:
suspend fun getUser(id: String): User = suspendCoroutine { continuation ->
Api.getUser(id) { user ->
continuation.resume(user)
}
}
If function someFunction()
doesn't call Api with callbacks then you should reconsider your approach getting rid of 'suspendCoroutine':
private suspend fun someFunction() {
// ...
val myResult = mySuspendingFunction()
// ...
}
If you still want to use suspendCoroutine
move call of mySuspendingFunction
out of suspendCoroutine
block:
private suspend fun someFunction(): String {
val myResult = mySuspendingFunction()
return suspendCoroutine { cont ->
//...
cont.resume(myResult)
}
}
suspend fun mySuspendingFunction(): String {
delay(1000) // simulate request
return "result"
}
It's best to avoid this and call the suspending function before suspendCoroutine
, as others have answered. That is possible for the specific case in question.
However, that is not possible if you need the continuation.
(The following is for those, who found this question for the this reason, as @Zordid and I have. chan.send
is an example of this.)
In which case, the following is a possible, but error prone way to do it, that I do not recommend:
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
})
}
}
(I think the error handling alone illustrates why it's not a great option, despite other problems.)
A better, safer and cheaper way is to use CompletableDeferred
if you can.
If you must pass in a Continuation, it's still safer and probably cheaper to do:
suspend fun cont2() {
val rslt = CompletableDeferred<Unit>()
chan.send(foo(Continuation(currentCoroutineContext()) {
rslt.completeWith(it)
}))
rslt.await()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With