Flow: the Basics Quiz

KOTLIN › Flow

What best describes a Kotlin Flow?

Answer: An asynchronous stream of values that arrive over time

A Flow models a stream of multiple values over time (updates, rows, results), unlike a suspend function that returns one value once.

Inside a flow { } builder, how do you hand a value to the collector?

Answer: emit(value)

emit(value) sends a value from the producer to the collector, one at a time.

You never collect f. What happens?

Answer: Nothing runs, a flow is lazy until a terminal operator collects it

Flows are cold and lazy: creating one only describes the stream. The producer runs only when a terminal operator like collect starts it.

Why must collect be called from inside a coroutine?

Answer: collect is a suspend function

collect is a suspend function, so it can only be called from a coroutine or another suspend function.

Two separate coroutines each collect the same cold flow. How many times does the producer block run?

Answer: Twice, once independently per collector

A cold flow re-runs its producer independently for every collector; nothing is shared by default.

Which is a genuine advantage of Flow over callback/listener APIs?

Answer: You write straight-line suspending code and get cancellation for free

The flow builder is a suspending context, so you call suspend functions inline (no callbacks), and collection is cancellable with the coroutine.

What can you legally do inside a flow { } block?

Answer: Call suspend functions like delay() or a network call, then emit their results

The flow builder is a suspending context, so you can call any suspend function and emit the results, which is what makes Flow so ergonomic.

Which of these is a terminal operator that starts the flow running?

Answer: collect

collect (like toList/first) is terminal, it actually drives the flow. map/filter/onEach are lazy intermediate operators that run nothing by themselves.

A cold flow is best described as…

Answer: Runs on demand when collected, independently per collector

Cold = nothing runs until collected, and each collector triggers its own fresh run. The opposite is a hot flow (always-on, shared).

You need a value that changes over time and is observed by the UI (e.g. current search results). Which shape fits?

Answer: A Flow that emits new results as they change

Values that change over time and are observed are exactly what a Flow (a stream) models; a single suspend return only gives you one snapshot.

Why might (1..1000).asFlow().collect { ... } not respond to cancellation promptly?

Answer: Because asFlow() does not use emit() internally, so there is no automatic cancellation check between elements

asFlow() converts the range directly without going through emit(). Since emit() is where flow builders check cancellation, asFlow() skips that check. Adding .cancellable() before .collect restores the cancellation check between elements.

How can you check for cancellation inside a flow { } builder between expensive operations, without calling emit()?

Answer: Call currentCoroutineContext().ensureActive() which throws CancellationException if the coroutine has been cancelled

currentCoroutineContext().ensureActive() checks whether the collecting coroutine is still active and throws CancellationException if not. It's the manual equivalent of the check that emit() does automatically.

Which builder is right for a flow of three known constants?

Answer: flowOf(1, 2, 3)

Nothing suspends, so the most specific builder wins. flow { } exists for producers that need to suspend, and the channel-backed builders for callbacks and concurrent emission.

What happens when you call toList() on a StateFlow?

Answer: It suspends forever, since a hot flow never completes

toList waits for completion, and a hot flow has no end. Use take(n).toList() for a bounded read or first() for the current value.

What does first() do once it has a value?

Answer: Cancels the upstream, which is what makes it safe on an infinite flow

Cancelling the producer is the only way a terminal operator over an infinite source can ever return, and it is why first is the idiomatic single-value read.

How many methods does the Flow interface declare?

Answer: One: a suspending collect

FlowCollector holds the other one, emit. Every operator is a flow whose collect wraps the upstream collector, which is why they compose so freely.

Why is a Flow cold?

Answer: Because the producer block is the body of collect, so each call re-runs it

Coldness is a consequence of the interface shape rather than an implemented feature, just as backpressure is a consequence of emit being suspending.

What distinguishes Sequence from Flow?

Answer: A Sequence blocks the thread while waiting; a Flow suspends

Both are cold and lazy. The compiler enforces the distinction by refusing suspend calls inside sequence { }, which is the language telling you which one you needed.

Which signature suits a one-off upload returning a confirmation?

Answer: suspend fun upload(f: File): Receipt

One question, one answer, no later updates. Returning a stream would hand the caller a lifecycle and an operator chain in order to deliver a single value.

Back to Flow: the Basics