Both conflate() and collectLatest help a slow consumer keep up with a fast producer. How do they differ in what they do with the in-progress work?
answer
- conflate = finish then skip (non-preemptive)
- collectLatest = cancel + restart (preemptive)
- conflate intermediate; collectLatest terminal
- collectLatest needs a suspension point to cancel
- Cheap renders -> conflate; expensive obsolete work -> collectLatest
basics
~10 sconflate() lets the current item finish, then jumps to the newest waiting value. collectLatest cancels the current work as soon as a newer value arrives and restarts with it. One finishes-then-skips, the other interrupts.
solid answer
~50 sBoth keep a slow consumer current with a fast producer, but they handle the *in-flight* item differently. conflate() never interrupts: the collector runs each value to completion, and only when it's free does it pick up the latest buffered value (dropping the intermediates). collectLatest does the opposite: when a new value arrives, it cancels the suspending block still processing the previous value and relaunches the block with the new value. So conflate is non-preemptive (no wasted-but-no-cancellation), while collectLatest is preemptive — it needs the work to be cancellable/suspending to actually abort early. Choose conflate when each render is cheap and atomic and you just want the freshest input; choose collectLatest when processing a value is expensive/long and a newer value makes the in-progress work obsolete (e.g., restart a network search on each keystroke). Note collectLatest is the terminal-operator sibling of conflate's intermediate-operator role.
go deeper
Knows both give 'latest wins' and that one finishes work while the other restarts.
Correctly states conflate finishes the current item while collectLatest cancels and restarts.
Explains preemptive vs non-preemptive, the suspension-point requirement for cancellation, and picks the right one by work cost.
Reasons about wasted compute, cancellation correctness, and designs the pipeline (cheap render vs cancel-and-restart) accordingly.
## The shared goal When a producer outpaces the collector, you usually only care about the **latest** value. Both `conflate()` and `collectLatest { }` deliver "latest wins", but they treat the **already-running** processing block differently. (This question lives at the conflate↔collectLatest boundary; the focus here is the contrast, not collectLatest's full mechanics.) ## conflate() — finish, then skip `conflate()` is an **intermediate** operator. It buffers one value (`buffer(1, DROP_OLDEST)`). The collector block runs **to completion** for whatever value it started; it is never cancelled. When it returns and asks for the next value, it gets the **latest** one currently buffered, and the intermediates that arrived meanwhile are dropped. - Non-preemptive: in-flight work is honoured. - Works even if the collector body is non-cancellable. ```kotlin flow { repeat(5) { delay(50); emit(it) } } .conflate() .collect { delay(200); println("done $it") } // each println completes ``` ## collectLatest — cancel and restart `collectLatest { }` is a **terminal** operator. Each emission launches the block in a coroutine; when a **newer** value arrives, the block processing the previous value is **cancelled** and a fresh one starts with the new value. ```kotlin flow { repeat(5) { delay(50); emit(it) } } .collectLatest { delay(200); println("done $it") } // earlier blocks get cancelled mid-flight ``` Because it relies on coroutine cancellation, the block must hit a **suspension point** (e.g. `delay`, a suspending call) for the cancel to take effect — otherwise a tight CPU loop runs to completion anyway. ## Side-by-side | Aspect | conflate() | collectLatest | |---|---|---| | Operator kind | intermediate | terminal | | In-flight value | runs to completion | cancelled when newer arrives | | Preemptive? | no | yes | | Needs cancellable work | no | yes (suspension point) | | Good for | cheap, atomic renders | expensive work made obsolete by newer input | ## Choosing between them - **conflate():** repaint a progress bar, draw the latest sensor value — each render is quick and you don't want to abort it. - **collectLatest():** restart an expensive autocomplete query on every keystroke — finishing the old query is wasted, so cancelling is correct. The practical risk of `conflate()` here: if each item is slow, you can still lag because conflate won't cancel the slow in-flight work; collectLatest would.
- Why might collectLatest fail to actually interrupt long work?Cancellation is cooperative — it only takes effect at a suspension point. A tight CPU-bound loop with no suspending call won't be cancelled and will run to completion despite a newer value arriving.
- Can you combine them, e.g. conflate() before collectLatest?It is redundant — collectLatest already handles latest-wins by cancelling, so conflate() in front adds little. Pick one based on whether you want to interrupt in-flight work.
conflate is a chef who finishes the current dish then takes the newest order; collectLatest scrapes the half-cooked dish into the bin the moment a newer order comes in.
saying these in an interview costs you the question
- Says both cancel in-flight work (only collectLatest does)
- Claims conflate() can interrupt a slow collector body
- Thinks collectLatest always aborts even CPU-bound work
- Confuses which is intermediate vs terminal