Flow Context, Errors & Backpressure Quiz

KOTLIN › Flow

What does context preservation mean for a collect lambda?

Answer: It runs in the context that called collect, whatever the flow does upstream

The collector owns its context and the flow may not change it, which is why a ViewModel collecting on Main needs no withContext around the lambda.

What produces IllegalStateException: Flow invariant is violated?

Answer: Calling emit from a different context than the builder is own, typically inside withContext

Emission must happen in the builder context so the collector context guarantee holds. The fix is flowOn, or a channel-backed builder if you truly must emit from elsewhere.

Which dispatcher does map run on here?

Answer: Dispatchers.Default, claimed by the flowOn below it

Each flowOn claims what is above it and not already claimed, so reading bottom-up: Default claims map, and IO claims the builder.

Why does adding flowOn also change buffering behaviour?

Answer: It inserts a channel to cross the thread boundary, so the upstream can run ahead

Crossing dispatchers requires a channel, and that channel is a buffer. So flowOn implies buffer, which is why timing can change when you add it.

An exception thrown inside a collect { } lambda is not caught by a catch placed last. Why?

Answer: catch handles upstream failures only, never the collector is own

Upstream-only is deliberate: it prevents catch from silently swallowing bugs in your own collector. Use an ordinary try/catch around the collect call.

What does retry(3) re-run on a cold flow?

Answer: The entire upstream from scratch, repeating any producer side effects

Retry re-collects the whole upstream, which for a cold flow means running the producer again. That is fine for an idempotent network read and a problem for a producer with side effects.

How does Flow implement backpressure?

Answer: emit suspends until the collector is ready, so the producer cannot run ahead

Suspension is the entire protocol, which is why a plain chain neither drops values nor queues without bound. buffer is the explicit opt-out when you want overlap.

What is conflate() equivalent to?

Answer: buffer(1, BufferOverflow.DROP_OLDEST)

That equivalence is why a plain buffer placed after conflate fuses into one configuration and silently removes the conflation.

What buffering does this chain actually have?

Answer: A single 50-element suspending buffer: the conflation is fused away

Adjacent buffer-family operators fuse into one channel configuration and the later call wins. To get both, ask for buffer(50, BufferOverflow.DROP_OLDEST) explicitly.

How do conflate() and collectLatest differ?

Answer: conflate drops values but lets the collector finish; collectLatest cancels the collector mid-work

The interruption is the distinction. collectLatest suits expensive cancellable collector work such as an in-flight request for a search query.

Why might (1..1000).asFlow().collect { } ignore cancellation?

Answer: It never suspends, so nothing checks for cancellation without cancellable()

The free check lives inside the flow builder emit. A non-suspending source has no such point, so you add cancellable() or check ensureActive() yourself.

When does onCompletion run?

Answer: On every ending: normal completion, failure, or cancellation

Its cause parameter is null on success and non-null otherwise, which makes it the right place for cleanup like hiding a spinner regardless of outcome.

Where should catch sit relative to onStart if it must also cover the loading emission?

Answer: Below onStart, since catch only sees operators above it

Field of view is strictly upstream, so anything you want covered has to be above the catch. Putting it last covers the whole chain except the collector.

Which builder should you use when values arrive from a callback that cannot suspend?

Answer: callbackFlow, with trySend and an explicit BufferOverflow policy

A callback cannot suspend to satisfy backpressure, so this is the one case where you make the drop-or-suspend decision explicitly. flow { } would throw the invariant violation.

What is operator fusion?

Answer: Adjacent flowOn, buffer and conflate merging into one channel configuration

These operators are the ones people stack, so the library merges them rather than allocating a channel each. The visible consequence is that a later buffer overrides an earlier conflate.

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.

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.

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.

Back to Flow Context, Errors & Backpressure