skip to content

Given a few real scenarios (parallel HTTP fan-out, ordered pagination, autocomplete), which flattening operator fits each and why?

level: middleimportance: should knowfreq 55%

answer

  1. Order matters -> concat
  2. Parallel + order-free -> merge
  3. Only newest -> latest
  4. Autocomplete = debounce + flatMapLatest
  5. Bound merge concurrency for fan-out

basics

~10 s

Autocomplete uses flatMapLatest (drop stale searches). Ordered pagination uses flatMapConcat (keep order). Parallel HTTP fan-out uses flatMapMerge with a concurrency cap (run several at once but bounded).

solid answer

~40 s

Pick by ordering and lifecycle needs. flatMapConcat when results must stay in upstream order or each step depends on the previous (sequential pagination, ordered transforms) — sequential, no parallelism. flatMapMerge when independent work should run in parallel and order doesn't matter (fan-out of many HTTP/IO calls) — set a sensible concurrency to bound resource usage. flatMapLatest when only the latest input matters and older work should be cancelled (autocomplete / search-as-you-type, reacting to a changing filter or selected id) — pair with debounce to reduce churn. A quick rule: care about order → concat; want parallelism → merge; want only the newest → latest.

code

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

// Autocomplete
val suggestions = queries.debounce(250L).flatMapLatest { api.search(it) }

// Ordered pages
val feed = (1..10).asFlow().flatMapConcat { api.page(it) }

// Parallel fan-out, bounded
val details = ids.asFlow().flatMapMerge(concurrency = 6) { api.detail(it) }

go deeper

for a junior

Maps the three canonical scenarios to the right operator.

for a middle

Justifies the choice via ordering and cancellation needs and adds debounce/concurrency tuning.

for a senior

Discusses resource bounding, dependent-step handling, and trade-offs between latency and correctness.

for a principal

Frames operator choice as a system design decision (pool limits, SLA, retry/error policy) across a pipeline.

## Decision framework Ask two questions: (1) Do I need results in upstream order? (2) Should new input cancel old work? | Need | Operator | |------|----------| | Ordered, sequential, dependent steps | `flatMapConcat` | | Parallel independent work, order irrelevant | `flatMapMerge(concurrency = N)` | | Only latest input matters, cancel stale | `flatMapLatest` | ## Scenario 1 — Autocomplete / search-as-you-type Each keystroke triggers a search; older requests are useless. Use `flatMapLatest` so a new query cancels the in-flight one. Add `debounce` to avoid firing on every character. ```kotlin queryFlow.debounce(300L) .filter { it.length >= 2 } .flatMapLatest { q -> api.search(q) } .collect(::render) ``` ## Scenario 2 — Ordered pagination / dependent steps Fetch pages in order, or run steps where step N+1 depends on N. Use `flatMapConcat` to keep order and avoid overlap. ```kotlin pageNumbers.flatMapConcat { page -> api.fetchPage(page) } .collect(::append) // pages appended in order ``` ## Scenario 3 — Parallel HTTP/IO fan-out Fetch details for many ids concurrently; order doesn't matter. Use `flatMapMerge` with a bounded `concurrency` so you don't open hundreds of connections at once. ```kotlin ids.asFlow() .flatMapMerge(concurrency = 8) { id -> api.fetchDetail(id) } .toList() ``` ## Why bound concurrency Unbounded parallel calls can exhaust connection pools, threads, or memory. The default cap (16) and an explicit `concurrency` keep fan-out safe. ## Common mistakes - Using `flatMapMerge` for pagination → out-of-order pages. - Using `flatMapConcat` for autocomplete → stale results pile up and lag. - Using `flatMapLatest` for fan-out → you only get the last id's result.

  • Why pair debounce with flatMapLatest in autocomplete?
    debounce drops rapid intermediate keystrokes so fewer searches start; flatMapLatest cancels any that do become stale, minimizing wasted requests.
  • What breaks if you use flatMapMerge for ordered pagination?
    Pages can arrive and be appended out of order because merge interleaves concurrent inner flows.

saying these in an interview costs you the question

  • Recommending flatMapMerge where order matters
  • Using flatMapLatest for fan-out (loses all but last)
  • Ignoring concurrency bounding for large fan-out
  • Not pairing debounce with flatMapLatest for typeahead
  • Claiming flatMapConcat can parallelise independent work

context