What am I missing here? ```flow { while (true)...
# coroutines
a
What am I missing here?
Copy code
flow {
    while (true) {
        withFrameMillis { frameTime ->
            emit(frameTime) // this will not work
        }
    }
}
withFrameMillis just has a very normal lamba. It must be possible to emit from that lambda right, since we're still in the flow-scope?
c
Think of it in terms of where the code is executing. the lambda of
withFrameMillis
is called by some other mechanism that’s managing time, on that mechanism’s thread. The lambda can access the
flow
scope, but that doesn’t mean it’s running in the same thread/coroutine context. What you need instead is
callbackFlow
so you can
trySend
a
Yes, that one
@Casey Brooks I think that makes sense. I'll try your suggestion
e
then it is as Casey says
in practice, on Android, the lambda is being executed on the UI thread from the Choreographer frame callback, not from within the
flow
(e.g. it's like switching dispatchers, except that the other context isn't really a "dispatcher" in the kotlinx.coroutines sense, but it's still different than the flow dispatcher)
c
In general, Flows run on the thread/coroutine context that collects them, because under-the-hood a call to
flow { emit() }
directly executes the lambda passed to
flow.collect { }
. Of course things get much more complicated when it adds operators,
.flowOn
, etc, but this is the general principle of Flows. For example:
Copy code
val collector = FlowCollector<Unit> { }
flow<Unit> {
    // `this === collector` is true
    emit(Unit)
}.collect(collector)
But any callbacks that get registered within that flow will probably be running on a different context, since it’s a completely separate API or async mechanism that will eventually invoke the lambda. So the only way to guarantee values from one callback-based API match the coroutine context of the emitter/collector, is to pass the values through a Channel, which is designed to facilitate communication between different coroutine contexts. Conceptually, this is basically what a callbackFlow is doing (though again, the details are a bit more complicated):
Copy code
val channel = Channel<Unit>(Channel.BUFFERED, BufferOverflow.SUSPEND)
withFrameMillis {
    channel.trySend(Unit)
}

flow<Unit> {
    for(value in channel) {
        emit(value)
    }
}
a
Amazing thanks!