skip to content

Explain what happens when the same cold Flow is collected twice. Do the two collectors share emissions or state?

level: middleimportance: must knowfreq 65%

answer

  1. Two collects = two independent runs
  2. No shared state or values between collectors
  3. Side effects repeat per collect
  4. Unicast by default; shareIn/stateIn for multicast
  5. SharingStarted controls upstream lifetime

basics

~10 s

Each collect runs the Flow's code again from scratch, completely independently. The two collectors do not share values or any internal state — it is like running the recipe twice.

solid answer

~40 s

Collecting a cold Flow twice triggers two separate, independent executions of the producer block. Each collect re-enters the flow {} lambda from the top, so any local variables, counters, network calls, or side effects happen once per collection. The collectors do not share emitted values — if the flow does a network request, it runs twice. This is the defining behavior of cold flows and contrasts with hot flows (SharedFlow/StateFlow) where multiple collectors observe one shared upstream. If you need fan-out (one execution, many subscribers) you convert the cold flow to hot using shareIn or stateIn with a CoroutineScope and a SharingStarted policy. A common bug is collecting an expensive cold flow in multiple places and unintentionally duplicating work.

code

kotlin · 6 lines
kotlin
val cold = flow { println("run"); emit(1) }
cold.collect {} // prints "run"
cold.collect {} // prints "run" again — second independent execution

// Share one run across collectors:
val hot = cold.shareIn(scope, SharingStarted.Lazily, replay = 1)

go deeper

for a junior

States that each collect re-runs the flow and they are independent.

for a middle

Explains unicast semantics, repeated side effects, and names shareIn/stateIn for fan-out.

for a senior

Discusses SharingStarted policies and the trade-off between duplicated work and sharing overhead.

for a principal

Designs where to introduce hot boundaries in an app to balance cost, lifecycle, and resource ownership.

## Two collects = two independent runs A cold `Flow` holds **no state and no buffered values**. Every call to a terminal operator (`collect`, `toList`, …) re-executes the producer block from the beginning in the collector's coroutine context. ```kotlin val counter = flow { var n = 0 while (n < 3) { emit(n++); } } counter.collect { print("A$it ") } // A0 A1 A2 counter.collect { print("B$it ") } // B0 B1 B2 — fresh counter ``` The variable `n` is local to each execution. The second collector gets its own `n` starting at 0; nothing is carried over. ## Side effects run once per collect Because the block re-runs, **side effects are repeated**: ```kotlin fun user(): Flow<User> = flow { emit(api.getUser()) // network call } val f = user() f.collect { ... } // network call #1 f.collect { ... } // network call #2 — duplicated work! ``` This is intentional: each collector is isolated and reproducible. But it is a frequent source of accidental duplicate network/database work. ## No sharing between collectors Cold flows give **unicast** semantics: one producer execution per collector. They are not multicast. If you want many collectors to share a single upstream run (multicast / fan-out), convert to a hot flow: ```kotlin val shared: SharedFlow<User> = user() .shareIn(scope, SharingStarted.WhileSubscribed(), replay = 1) ``` - `shareIn` keeps one upstream collection and broadcasts to all subscribers. - `stateIn` does the same but exposes the latest value as a `StateFlow`. - `SharingStarted` controls when the shared upstream starts/stops (`Eagerly`, `Lazily`, `WhileSubscribed`). ## Why this design Coldness makes flows **composable and lazy**: an operator chain is just a description, and each subscriber gets a clean, independent run. Sharing is then an explicit opt-in, so you decide consciously when to pay for fan-out versus duplicated work.

  • How do you make two collectors share a single upstream execution?
    Convert the cold flow to hot with shareIn (SharedFlow) or stateIn (StateFlow), supplying a CoroutineScope and SharingStarted policy.
  • What is the risk of collecting an expensive cold flow in several places?
    Each collection re-runs the producer, duplicating the expensive work (e.g., the network call fires once per collector).

Like photocopying a recipe and giving one to each cook — each cooks the whole dish independently, nothing is shared.

saying these in an interview costs you the question

  • Claiming collectors share the same emissions by default
  • Assuming the Flow caches the first run's values
  • Not knowing shareIn/stateIn convert cold to hot
  • Thinking a second collect resumes where the first left off

context