I want to know a practical scenario of both of them. I know the difference but couldn't relate to my implementation.
Collect will collect every value , and CollectLatest will stop current work to collect latest value,
The crucial difference from collect is that when the original flow emits a new value then the action block for the previous value is cancelled.
flow {
emit(1)
delay(50)
emit(2)
}.collect { value ->
println("Collecting $value")
delay(100) // Emulate work
println("$value collected")
}
prints "Collecting 1, 1 collected, Collecting 2, 2 collected"
flow {
emit(1)
delay(50)
emit(2)
}.collectLatest { value ->
println("Collecting $value")
delay(100) // Emulate work
println("$value collected")
}
prints "Collecting 1, Collecting 2, 2 collected"
So , if every update is important like state, view, preferences updates, etc , collect should be used . And if some updates can be overridden with no loss , like database updates , collectLatest should be used.
delay
represents here? Is it a network call or a database access? –
Kaon .conflate()
+ collect{}, does it do the same or different? –
Sodden collectLatest
. See my answer –
Monologue i think this article made it simple with example.
The tradeoff between collect { ... } and collectLatest { ... } is simple. when you need to process all the values you receive, you should use collect, and when you want only the latest value to be processed, use collectLatest.
Before talking about what collect
and collectLastest
can do for us, let's recall what the goal of a flow is. A flow does, long story short, the same as what a suspend function does, except that it does that for multiple values. So when we want to return multiple values we return a flow of values instead of one single value via a suspend function.
Both collect functions do something different. They process all emitted values and return nothing.
When collect
gets called, all emitted values will be processed. So for example, when collecting realtime data of the temperature in a room, we may want to process all of this data sequentially.
Considering function:
private suspend fun processTemperature(value: Int) {
println("Processing-> $value")
delay(Random.nextLong(1, 5))
println("Processed!-> $value")
}
For this we can use collect
like this:
flow {
repeat(10) {
emit(Random.nextInt(10, 25))
delay(2)
}
}.collect { value ->
processTemperature(value)
}
println("Finished processing collect!")
This will process all values and this is interesting for example if we want to keep a full history of all temperatures and calculations. However if for example the system fails or starts failing, we just want to process a temperature if processing is under 2 milliseconds. For this we can use collectLatest
. Because a new action block cancels any running collect block, we can guarantee that collectLatest
will get to process only temperatures that take less than 2 milliseconds to process:
flow {
repeat(10) {
emit(Random.nextInt(10, 25))
delay(2)
}
}.collectLatest { value ->
processTemperature(value)
}
println("Finished processing collectLatest!")
collectLast
isn't about collecting the last emitted value. It is more about cancelling the current action block when a value is emitted and that value is what is meant by last which can be useful in our example. And the reason is of course, and with other words, to keep the reading pace timed equally regardless of what happens during collection.
I would not expect a lot of use cases for collectLast
in general but for more scientific things like this example they can be very usefull.
Find more here:
You can find the original code on my example here
processTemperature
; specifically what suspend calls it has within it and how many. See my answer for more details. –
Monologue An important detail missing from the other answers is that Kotlin coroutine cancellation is cooperative. That means the thing being cancelled determines how it can be cancelled, possibly preventing cancellation entirely!
Since collectLatest
works by cancelling the previous collect block, it is crucial to consider what you are putting in that block. As a basic example of how this can go wrong:
flow {
emit(1)
delay(50)
emit(2)
}.collectLatest { value ->
println("Collecting $value")
Thread.sleep(100) // Emulate non-suspending work
println("$value collected")
}
here collectLatest
will behave identically to collect
. Non-suspending functions are never cancellable, so even though it will cancel the block, cancellation will be ignored and the function will run to completion. The only way to fix this is by introducing suspending calls (yield is always an option) or by checking the isActive
property available in coroutines.
If your block is cancellable (because it contains suspending calls or is manually checking isActive
), you should also consider what would happen if it were cancelled. For example, you wouldn't want to leave your software in an invalid state because it got interrupted. You might want to wrap everything in the block with a try { ... } finally { ... }
if you need to perform any final cleanup when cancelled.
© 2022 - 2025 — McMap. All rights reserved.