Flow & Operators Quiz

KOTLIN › Concurrency

Two coroutines each call collect on the same cold Flow built with 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).

Given upstream.map { }.flowOn(Dispatchers.IO).collect { }, where does the collect lambda run?

Answer: In the context of the coroutine that called collect

flowOn is context-preserving and only affects the upstream (the map and producer). Downstream operators and the collector run in the collector's own context.

Which statement about the catch operator is correct?

Answer: It only catches exceptions emitted by the upstream flow above it

catch is restricted to upstream exceptions. It cannot see the collect lambda or downstream operators, and inside it you may rethrow or emit a fallback.

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

Answer: flatMapLatest, which cancels the previous request on each new query

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.

Inside a flow { } builder, why does wrapping emit() in withContext(Dispatchers.IO) fail?

Answer: It breaks Flow context preservation; emit must stay in the builder context

Flow enforces context preservation and throws if emit happens in a different context. Use flowOn to change the producer's dispatcher, or channelFlow/callbackFlow to emit from elsewhere.

A slow collector processes a fast producer, but you only ever need the most recent value and want to drop the ones in between. Which operator do you use?

Answer: conflate

conflate keeps only the latest value, skipping intermediates the collector couldn't keep up with. buffer would queue all of them; distinctUntilChanged only removes consecutive duplicates.

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

Answer: toList, a terminal operator that collects the flow and starts it

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.

You wrap a callback-based location API as a Flow using callbackFlow. What must the builder block do at the end to behave correctly?

Answer: Call awaitClose { } to wait for cancel and remove the callback

callbackFlow requires awaitClose at the end; it keeps the producer alive until the collector cancels and provides a place to remove the listener. Omitting it throws IllegalStateException at runtime.

Using collectLatest, what happens when a new value arrives before the previous collector block has finished processing?

Answer: The running block is cancelled and restarted with the new value

collectLatest cancels the in-progress collector block whenever a newer value is emitted and relaunches it with that value, so only the processing of the latest value runs to completion.

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.

You want to convert a cold Flow into a hot stream that always holds a current value and replays it to every new collector while sharing a single upstream collection. Which operator do you use?

Answer: stateIn

stateIn turns a cold flow into a StateFlow that always has a current value and emits it to new subscribers. shareIn produces a SharedFlow without an always-present value, and buffer does not make the flow hot at all.

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, collecting inner flows one at a time in upstream order

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.

An exception is thrown inside your collect { } lambda. Why won't a catch operator placed above collect handle it, and what should you do?

Answer: Wrap collect in try/catch; catch only sees upstream failures

catch enforces exception transparency and only intercepts failures from upstream operators and the producer, never the collect lambda. To handle errors in the collector itself, wrap the collect call in a regular try/catch.

Back to Flow & Operators