https://kotlinlang.org logo
Title
o

Oleg Siboglov

03/30/2021, 11:19 PM
Hey everybody. I was hoping I could get some help in how to best approach this situation… I have a suspending function that returns some data. I may need to suspend the coroutine that called the function while something else is done asynchronously, and then resume the coroutine, eventually returning data from that function.
suspend fun foo(): Bar {
    // I call another suspend function here returning an intermediate value.
    if (error) {
        suspendCancellableCoroutine<Bar> { continuation ->
            val listener = object : Listener {
                override fun callback() {
                    // I need to call this suspend function again.
                    // However, here is where I get an error.
                    continuation.resume(foo())
                }
            }
            // I call other functions that will eventually call the listener.
        }
    }
}
The error that I get is obvious -
Suspension functions can be called only within coroutine body
. However, I’m not really quite sure on how to resume the coroutine properly. Any help is much appreciated. Thank you.
e

efemoney

03/31/2021, 12:11 AM
I guess its pretty clear that the issue is that the context that
callback
runs on is not suspending so you cannot call suspending functions inside of it. One thing you can do is use
runBlocking
to wrap the inner
foo
function. Whoever calls outer
foo
is still suspended and when the inner runBlocking returns, outer foo’s caller should be resumed (as intended). But you should also restructure your code. This is what it looks like to me:
class Bar

fun interface Listener {
  fun callback()
}

suspend fun doIntermediateStuff(): Boolean = TODO()

suspend fun suspendForCallback() = suspendCancellableCoroutine<Unit> { continuation ->
  val listener = Listener {
    continuation.resume(Unit)
  }
  // someOtherCode.register(listener)
}

suspend fun foo(): Bar {
  val isError = doIntermediateStuff()

  if (isError) {
    suspendForCallback()
    return foo()
  }

  return Bar()
}
c

Casey Brooks

03/31/2021, 3:17 PM
Another option, rather than using
suspendCancellableCoroutine
would be a
callbackFlow
, which does allow suspending functions to be called within with block
u

uli

04/02/2021, 9:49 AM
As a rule of thumb, do not mix any further logic into suspendCancelableCoroutine. Wrap your callback into a suspend function Aa a single concern. And then solve your business logic with suspend functions
1