How do buffer, conflate, and collectLatest manage Kotlin Flow backpressure?
Coroutine flow control and suspension-vs-dropping tradeoffs.
Backpressure is fast emit vs slow collect; buffer() suspends the producer when full, conflate() drops stale values, and collectLatest() cancels active collection to…
WHAT THIS TESTS: This question probes whether you understand how Kotlin Flow handles the mismatch between a fast upstream producer and a slow downstream collector without blocking threads. It tests your knowledge of coroutine suspension, channel buffering, and the difference between lossless queuing, lossy dropping, and preemptive cancellation. A senior candidate should be able to map each operator to a specific backpressure strategy and justify when to use one over the other.
A GOOD ANSWER COVERS: First, define backpressure in Flow terms: when an emitter calls emit faster than the collector can process, the default behavior is that emit suspends until collect is ready, which is safe but can slow the producer. Second, explain that buffer() interposes a Channel with a configurable capacity between the emitter and collector; the emitter can run concurrently and only suspends when the buffer is full, making it a lossless strategy that trades memory for throughput. Third, describe conflate() as a lossy strategy that keeps only the latest value; if the collector is busy, intermediate emissions are dropped, which is ideal when stale data is worthless, such as UI state updates. Fourth, clarify that collectLatest() is also lossy but uses cancellation instead of dropping: each new emission cancels the current collect block via structured concurrency, so you never process an outdated value, which suits search debouncing or location updates. Fifth, mention that buffer and conflate can be combined, for example buffer(CONFLATED), to fine-tune behavior.
COMMON WRONG ANSWERS: A major red flag is claiming that Flow implements Reactive Streams backpressure with explicit request(n) semantics; Flow uses coroutine suspension, not demand signals. Another error is stating that buffer() drops values by default; the default is to suspend the emitter, not discard emissions. Candidates sometimes say conflate() and collectLatest() are identical, but conflate() drops values silently while the collector runs, whereas collectLatest() actively cancels the collector. Saying that any of these operators create new threads is also wrong; they rely on coroutine dispatchers, not thread creation.
LIKELY FOLLOW-UPS: The interviewer may ask how buffer() interacts with different CoroutineDispatcher configurations, or when you would prefer conflate() over collectLatest() in a real Android UI layer. They might also ask about the difference between Channel.CONFLATED and Flow.conflate(), or how to implement a custom operator that samples rather than conflates. Another common follow-up is how backpressure propagates across multiple flow operators in a chain.
ONE CONCRETE EXAMPLE: Imagine a location sensor emitting GPS coordinates every one hundred milliseconds, but the map rendering coroutine takes two hundred fifty milliseconds per frame. Without an operator, emit suspends and the sensor stalls. Using buffer(10) queues up to ten fixes so the sensor keeps emitting briefly, but memory grows if the slowdown persists. Using conflate() means the renderer always gets the most recent coordinate and skips the stale ones in between, keeping the map smooth without lag. Using collectLatest() means if a new coordinate arrives while the previous frame is still rendering, the ongoing render job is cancelled and restarted with the fresh data, preventing wasted work on outdated positions.
Read the original → kotlinlang.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.