Flow Operators Quiz

KOTLIN › Flow

Two coroutines each collect the same cold flow. What happens?

Answer: The producer block runs independently for each collector

Cold flows are lazy: each terminal operator triggers a fresh, independent execution of the producer. Sharing requires shareIn/stateIn (hot flows).

You want a search field to cancel the previous network request whenever a new query arrives. Which operator fits best?

Answer: flatMapLatest

flatMapLatest cancels the previous inner flow when a new upstream value is emitted, which is exactly the search-as-you-type pattern. flatMapConcat would queue them instead.

What is the key behavioral difference between zip and combine?

Answer: combine emits latest values on any update; zip matches items one by one

combine fires whenever either source emits (after both have produced at least one value), using the latest of each. zip strictly pairs values and waits for both.

Which of the following is a terminal operator that actually triggers collection of the upstream flow?

Answer: toList

toList is a terminal suspend operator that collects the flow into a list, starting execution. map, filter, and onEach are intermediate operators that return a new Flow lazily without running anything.

In a search box you want to wait until the user pauses typing for 300ms before firing a query, discarding intermediate keystrokes. Which operator fits best?

Answer: debounce

debounce(300) only emits a value once that quiet window elapses with no new emissions. sample emits the latest value on a fixed interval, and distinctUntilChanged only filters consecutive duplicates.

flatMapConcat, flatMapMerge, and flatMapLatest all flatten a flow of flows. Which one collects the inner flows one at a time, preserving upstream order and waiting for each to complete?

Answer: flatMapConcat

flatMapConcat processes inner flows sequentially in upstream order, fully collecting one before starting the next. flatMapMerge runs them concurrently without ordering, and flatMapLatest cancels the previous inner flow.

What does this produce?

Answer: [1, 10, 2, 20], because transform may emit any number of values per input

transform emits once per call to emit, in order, per input value. map and filter are the special cases where that count is fixed at one or at most one.

You apply debounce(1000) to an accelerometer emitting every 20ms. What is collected?

Answer: Nothing, because a full second of silence never occurs

debounce waits for a quiet window that a continuous feed never provides. sample is the operator for that case, since it emits on a timer instead of waiting for a gap.

Which operator fires a search only once the user stops typing?

Answer: debounce(300)

Typing is a burst that ends, so waiting for silence is exactly right. sample would fire every 300ms while they are still typing, which is what you were trying to avoid.

Why might mapLatest { heavyCpuLoop(it) } cancel nothing?

Answer: The loop has no suspension points, so cooperative cancellation has nowhere to act

The Latest operators request cancellation and cancellation is cooperative, so a block that never suspends or checks isActive runs to completion regardless.

What happens here?

Answer: It prints 0, 1, 2 and completes, because take cancels the upstream

take cancels the producer once it has its quota, via an internal AbortFlowException it catches itself. That is the only way collecting an infinite source could terminate.

An upstream flow throws and onCompletion logs the non-null cause. Does the collector still see the exception?

Answer: Yes: onCompletion observes without handling, so the flow still fails

onCompletion is a side-effect hook, not a recovery point. Only catch can handle a failure and emit a fallback in its place.

Which operator recomputes whenever either of two inputs changes, using the latest of both?

Answer: combine

combine keeps the latest from each side and re-emits whenever either changes. zip would wait for a matching pair, so a change on one side alone would produce nothing.

What is flow.onEach { }.launchIn(scope) equivalent to?

Answer: scope.launch { flow.collect { } }

It is a launch wrapped around a collect, returning a Job rather than suspending. Putting the work in onEach is the idiom that follows, because launchIn accepts no lambda.

Why does code placed after collect on a hot flow never execute?

Answer: collect suspends until the flow completes, and a hot flow never does

collect is a suspend function that returns only when the flow finishes. launchIn exists precisely so the collection runs in its own coroutine and the caller continues.

Which operator emits at most once per interval from a feed that never goes quiet?

Answer: sample(1000)

sample snapshots the latest value on a timer, which works regardless of whether the source pauses. debounce would emit nothing at all from a continuous source.

When does onEmpty fire?

Answer: When the upstream completes having emitted nothing at all

It is the declarative way to express a no-results state, replacing a boolean tracked inside the collector.

What does onEach do to the value passing through it?

Answer: Nothing: it runs a side effect and passes the value on unchanged

That is why it suits logging and analytics, and why it is the operator launchIn pairs with: the work happens as a side effect rather than as a transformation.

Back to Flow Operators