skip to content

What does Flow's collectLatest do, and how is it different from a plain collect?

level: juniorimportance: must knowfreq 70%

answer

  1. collect = finish every value; collectLatest = cancel previous, run latest
  2. Cancellation is cooperative — needs a suspension point
  3. Only the latest emission completes
  4. Great for live search / latest UI state
  5. Action runs in a child coroutine that gets cancelled

basics

~20 s

collectLatest runs your handling code for each value, but if a new value arrives before the previous one finishes, it stops the previous work and starts on the new value. Plain collect always finishes every value first.

solid answer

~40 s

collect calls your block for every emitted value and fully runs it before processing the next emission; it never drops work. collectLatest also calls your block per value, but when a newer value arrives it cancels the still-running block (the coroutine running your action) and restarts it with the latest value. So only the latest value's processing is guaranteed to complete; intermediate slow handlers may be cancelled mid-flight. The cancellation is cooperative: your block must hit a suspension point (delay, a suspend call, withContext) for cancellation to actually take effect. Use collectLatest when only the freshest value matters — e.g. live search, UI state — and stale in-progress work should be abandoned. Use collect when every value must be handled.

code

kotlin · 10 lines
kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    flowOf(1, 2, 3).collectLatest { v ->
        println("begin $v")
        delay(50)
        println("finish $v") // only prints finish 3
    }
}

go deeper

for a junior

Knows collectLatest cancels the previous block and only the latest value finishes.

for a middle

Explains the cooperative-cancellation caveat and a real use case like live search.

for a senior

Frames it as consumer-side conflation, contrasts with collect and conflate, knows the child-coroutine mechanism.

for a principal

Reasons about when abandoning stale processing is correct vs. dangerous (side effects, non-idempotent work) and picks the operator accordingly.

## The terms - A **Flow** is a cold asynchronous stream that emits values over time. - A **terminal operator** (like `collect`) starts the flow and consumes its values. - `collect { value -> ... }` is the basic terminal operator: it suspends, and for **every** emitted value it runs your lambda (the *collector block* / *action*) **to completion** before the producer is allowed to emit the next value. ## What collectLatest changes `collectLatest { value -> ... }` also runs your block per value, but with one rule: **when a new value arrives while your block is still running for the previous value, the previous block is cancelled and a fresh one starts with the new value.** Only the block for the **latest** emitted value is allowed to run to completion. Mechanically, the action runs in its own child coroutine. On each new upstream emission, `collectLatest` cancels that child and launches a new one. This is structured concurrency — the cancelled block stops at its next suspension point. ## Cooperative cancellation caveat Kotlin coroutine cancellation is **cooperative**: a coroutine only actually stops at a **suspension point** (`delay`, another `suspend` call, `withContext`, `yield`) or when it explicitly checks `isActive` / calls `ensureActive()`. If your block is doing tight CPU work with no suspension, `collectLatest` cannot interrupt it — the old block runs to the end anyway. ```kotlin flow { emit(1); emit(2); emit(3) }.collectLatest { value -> println("start $value") delay(100) // suspension point — cancellable println("done $value") // only prints for the last value, 3 } // Output: start 1, start 2, start 3, done 3 ``` Because 1 and 2 are superseded before their `delay` completes, only `done 3` prints. ## When to use it - **Live search / autocomplete:** each keystroke supersedes the previous query. - **UI rendering** of the latest state where stale frames are pointless. - It is a **conflation-like** policy at the consumer side: it does not drop values arbitrarily, it just abandons *processing* of superseded values. If you must handle every value, use `collect`. If you only need to keep the freshest value but never cancel a running block, `conflate()` (a sibling operator) is the other tool.

  • If the collector block has no suspension point, does collectLatest still cancel it early?
    No. Cancellation is cooperative; without a suspension point or isActive/ensureActive check the old block runs to completion before the next starts.
  • Does collectLatest drop emitted values?
    It does not drop the values themselves — it always starts the block for every value. It cancels the *processing* of superseded values mid-flight.

Like a chef who throws out the half-cooked dish the moment a newer order replaces it — only the most recent order ever gets plated.

saying these in an interview costs you the question

  • Saying collectLatest drops/skips emissions instead of cancelling in-flight processing
  • Claiming cancellation is immediate/preemptive rather than cooperative
  • Thinking it runs blocks in parallel rather than cancel-and-restart
  • Confusing it with conflate (which drops intermediate values, no cancellation)

context