I have a little bit philosophical question. I’m using StateFlow
for returning the processing state of my API, it does exactly what I want. To provide the latest state at any time, even in the time when processing is already done. I just don’t like one thing.
When I use this:
progressFlow.collectLatest { ... }
It is never finished, so the parent’s job is also never finished. I expect that this is an issue also from a resources point of view, right?
So I always must somehow handle the final state like by takeWhile()
, which then skips the last element or us this
progressFlow.transformWhile { state ->
emit(state)
state !is ScannerState.FinishStatus
}.collect { state -> ... }
I don’t like the fact that all users of this API (this StateFlow
) need to care about it and need to repeat this check.
Is there any better way?
I can use normal flow and cancel it. But I will lose the possibility to get the last value even when processing is done and also I will lose the diret access value
.
So I would like to keep all benefits of StateFlow
, but I just want to tell as API author, that there will be no more updates and all consumers can stop waiting on it, but all of them still can get the latest final state.
So, I also wanted takeWhile
to emit the last value so I made an extension function takeUntil
with the code you described passing the condition as a function.
Anyway, if you want consumers to not worry about doing such a check, only let them consume the resulting flow. That is, make your MutableStateFlow
private, and expose the result of your tansformWhile
as a Flow
via something like val exposedFlow: Flow<Type> = mutableFlow.transformWhile{...}
1 Like
Yes. I was thinking about returning the resulting flow in a such way. Maybe it is a good idea. It will just lose the possibility to call quick access to the current value
that StateFlow has.
If you are only interested in the final value, you can call last
on the flow to get the last value from the flow. If you are interested in every value, collect
can take a lambda that is invoked for every value emitted to the flow.
You only loose access to .value
externally, internally you’ll still have access to it. AFAIK there isn’t really a good reason to have that exposed externally anyway (because as explained above you can get each value as it is emitted anyway)
Since StateFlow
s are hot, by definition they never complete. The collector would need to cancel the job of the coroutine used to collect it.
val job = scope.launch {
progressFlow.collectLatest { state ->
...
if (state is ScannerState.FinishStatus) {
job.cancel()
}
}
}
1 Like