Is it possible to use channels to wait for a signa...
# coroutines
r
Is it possible to use channels to wait for a signal to do work? Something like:
Copy code
suspend fun waitForUpdates(): ReceiveChannel<Update> = produce {
  onUpdateHook { update ->
    send(update)
  }
}

fun startWaitingForUpdates() {
  launch {
    waitForUpdates().onReceive { update ->
      doUpdate(update)
    }
  }
}
s
Absolutely. Be sure that the `produce`’s lambda does not return to keep the channel open. (see the
suspendCoroutine
call in
waitForUpdates
.
Copy code
fun CoroutineScope.waitForUpdates(): ReceiveChannel<Update> = produce {
    onUpdateHook { update ->
        send(update)
        suspendCoroutine<Nothing> { /* do nothing */ }
    }
}

fun CoroutineScope.startWaitingForUpdates() {
    launch {
        waitForUpdates().apply {
            select {
                onReceive { update ->
                    doUpdate(update)
                }
            }
        }
    }
}
r
suspendCoroutine<Nothing> { /* do nothing */ }
^ this right here was the missing link, thank you!
s
If you need to be able to close the channel on a failure/update-close signal, use something like this: E.g.:
Copy code
fun CoroutineScope.waitForUpdates(): ReceiveChannel<Update> = produce {
    suspendCoroutine<Unit> { cont ->
        onUpdateHook { update ->
            send(update)
        }
        onUpdateTermination { error ->
            if (error != null) {
                cont.resumeWithException(error)
            }
            else {
                cont.resume(Unit)
            }
        }
    }
}
r
Great! Thanks so much!
d
send
won't work in some of these contexts, use
offer
with a conflated channel.
r
yeah, I was just realizing that I lose my coroutine body in the context of the lambda
s
It would depend on how the signature of
onUpdateHook
looks like. You can make its lambda-parameter
suspend
(and make it an extension-function of CoroutineScope) if synchronization is important. Otherwise, just call
offer
like Dico said 🙂
r
Unfortunately, I don’t have control over
onUpdateHook
. Thanks for all the help!
A quick follow-up question, is there a way to use something like
produce
without a
CoroutineScope
? I’m trying to get my repository interface to return a
channel
, but if I declare
waitForUpdates()
as an extension of
CoroutineScope
, I can’t call the function unless it’s from within the context of the repository object
b
You could manage the lifecycle of the channel yourself. Channels can be created using the
Channel()
function
produce
and
actor
are implementations that ensure that the Channel's lifecycle is tied to the lifecycle of your coroutine so it doesn't leak
r
How would I go about tying my channel’s lifecycle to the lifecycle of a coroutine in which it’s containing
suspend
function is invoked?
s
suspend fun waitForUpdates(): ReceiveChannel<Update> = coroutineScope { ... }
. The lambda of the
coroutineScope
has a
this
receiver that is a
CoroutineScope
instance.
💯 1