https://kotlinlang.org logo
#coroutines
Title
# coroutines
d

Dmytro TOLSTYI

01/21/2019, 7:59 AM
Hi guys. I got a strange situation with
suspend
function which I can't solve: I got function
suspend fun one()
which executes some code and then calls a function that accepts a callback function as a param. But, this param function is not suspend. The issue is that I need to call another suspend fun from that callback param function. Here's an example:
Copy code
suspend fun one() {
    doSomething()
    funWithNonSuspendCallback("data") {
        two() // I need to call it from here but this is not suspend scope anymore
    }
}

suspend fun two() {
}
u

uli

01/21/2019, 8:11 AM
try wrapping funWithNonSuspendCallback into a coroutine: https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#wrapping-callbacks After that your code will look like that:
Copy code
suspend fun one() {
    doSomething()
    suspendingWrappedFunWithNonSuspendCallback("data")
    two() // Back into coroutine scope after callback was called
    }
}
d

Dmytro TOLSTYI

01/21/2019, 8:16 AM
@uli Thank you Uli. I will take a look into this solution (I didn't know about this feature)
l

louiscad

01/21/2019, 8:29 AM
@Dmytro TOLSTYI Just use this:
Copy code
suspendCoroutine { continuation ->
    // call continuation.resume(Unit) when callback is invoked
}
If you support cancellation, you can also use this;
Copy code
suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
        // unregister callback
    }
    // call continuation.resume(Unit) when callback is invoked
}
d

Dmytro TOLSTYI

01/21/2019, 9:02 AM
Thank you @louiscad
@uli @louiscad But
suspendCoroutine
will create a new suspension point, right? It means that my code will be blocked until this part is executed. This is not exactly what is callback function flow...
l

louiscad

01/21/2019, 9:09 AM
@Dmytro TOLSTYI Your code will not block, it will suspend (hence the name), until
resume
is called back on the
Continuation
. You register the callback in the lambda passed to
suspendCoroutine
or
suspendCancellableCoroutine
, which will be called immediately, and will then suspend waiting for your callback to invoke
resume
.
d

Dmytro TOLSTYI

01/21/2019, 9:16 AM
OK. Maybe block was incorrect word, I just tried to explain the flow:
Copy code
fun one()
suspendCoroutine {}
fun two()
In this case
fun two()
is not going to be executed unless
suspendCoroutine
is not resumed, right?
@louiscad Right?
l

louiscad

01/21/2019, 9:45 AM
@Dmytro TOLSTYI Your snippet is not syntactically correct if I follow what you're saying, hence my misunderstanding. If you mean such a snippet:
Copy code
one()
suspendCoroutine { c ->
    // Some code
}
two()
then yes, just like all suspend function calls, it will suspend until it's resumed, and only then,
two()
wil be executed.
d

Dmytro TOLSTYI

01/21/2019, 9:50 AM
@louiscad But in case of binding event listener variant:
Copy code
one()
bindEventListenerWithCallback(callback)
two()
The flow is
one()
->
bindEventListenerWithCallback()
->
two()
->
callback()
Looks like the flow is different in this case
l

louiscad

01/21/2019, 9:52 AM
I think I'm missing some context here, and I don't see any call to
callback()
in your snippet…
d

Dmytro TOLSTYI

01/21/2019, 9:52 AM
@louiscad
callback
is going to be called when appropriate event is fired
@louiscad Some time in future.
l

louiscad

01/21/2019, 9:54 AM
@Dmytro TOLSTYI If
bindEventListenerWithCallback
is a suspend function but behaves the way you tell me, then it's not implemented correctly (using
suspendCoroutine
or
suspendCancellableCoroutine
properly should fix this)
d

Dmytro TOLSTYI

01/21/2019, 10:04 AM
@louiscad
bindEventListenerWithCallback
is not a
suspend
function. What it does is just register handler that will be called when some kind of event occurs
l

louiscad

01/21/2019, 10:14 AM
@Dmytro TOLSTYI So you need to wrap it and the callback you pass to it into
suspendCoroutine
or its cancellable version if supported.
u

uli

01/21/2019, 11:34 AM
Like this:
Copy code
suspend fun suspendingWrappedFunWithNonSuspendCallback(data: String): Value = suspendCoroutine { cont ->
    funWithNonSuspendCallback("data") { cont.resume(it) }
}
1
d

Dmytro TOLSTYI

01/22/2019, 4:54 AM
Thank you for the help. I will try to implement it in my case