skip to content

What is a Kotlin Flow, and how does it relate to a regular list and to reactive streams like RxJava's Observable?

level: juniorimportance: must knowfreq 80%

answer

  1. List = all values now; Flow = values over time
  2. Sequence is lazy+sync; Flow is lazy+async (can suspend)
  3. Flow = Kotlin's Observable/Flux, built on coroutines
  4. emit() pushes, collect() pulls/runs
  5. Suspension instead of callbacks/Schedulers

basics

~10 s

A Flow is a stream of values produced over time, one after another. Unlike a list, the values can arrive asynchronously without blocking. It is Kotlin's coroutine-based answer to reactive streams.

solid answer

~40 s

Flow<T> is an asynchronous data stream that emits zero or more values of type T over time and then completes (or fails). A List<T> holds all values at once in memory; a Flow produces them lazily, possibly asynchronously, using suspend functions so it never blocks a thread. It plays the same role as RxJava's Observable or Reactor's Flux: representing a sequence of events you react to. The key Kotlin difference is that Flow is built on coroutines and structured concurrency instead of callbacks and Schedulers — emission and collection happen inside coroutines, and suspension (not buffering or callbacks) is the primitive used to coordinate producer and consumer speed.

code

kotlin · 8 lines
kotlin
val temps: Flow<Int> = flow {
    while (true) {
        emit(readSensor())  // suspend network/IO call, no blocking
        delay(1000)
    }
}

temps.collect { temp -> println("Now: $temp") }

go deeper

for a junior

Knows Flow is an async stream of values over time, unlike a List, and that collect consumes it.

for a middle

Articulates the Sequence (sync, can't suspend) vs Flow (async, can suspend) distinction and maps Flow onto Observable/Flux.

for a senior

Explains that suspension (not callbacks/Schedulers) is the coordination primitive and ties Flow to structured concurrency.

for a principal

Frames Flow as a design choice: pushing reactive semantics onto suspend/coroutines to unify async code, with implications for tooling, debugging, and interop with Rx/Reactor.

## What a Flow is `Flow<T>` (from `kotlinx.coroutines.flow`) is an **asynchronous sequence**: it produces a stream of values of type `T`, in order, over time, and then either completes normally or completes with an exception. It is the coroutine-native equivalent of a reactive stream. ## Flow vs List vs Sequence - A `List<T>` is **eager and finite**: every element already exists in memory. - A `Sequence<T>` is **lazy but synchronous**: elements are computed on demand, but each step runs on the calling thread and **cannot suspend** (you can't call `suspend` functions inside a sequence's `yield`). - A `Flow<T>` is **lazy and asynchronous**: producing the next element may run a `suspend` function (e.g. a network call) without blocking the thread. ## Flow vs reactive streams (Rx/Reactor) Flow occupies the same niche as RxJava's `Observable`/`Flowable` or Project Reactor's `Flux`: a push-style stream of events you transform with operators (`map`, `filter`, etc.). The conceptual differences: - Flow uses **coroutines and `suspend`** for asynchrony, so there are no callbacks and no `Scheduler` objects. - Backpressure is handled implicitly by **suspension**: a slow collector simply suspends the producer. - Flow is integrated with **structured concurrency**, so cancellation and scope are inherited automatically. ```kotlin import kotlinx.coroutines.flow.* val numbers: Flow<Int> = flow { for (i in 1..3) { emit(i) // push a value to the collector } } // nothing runs until someone collects: numbers.collect { println(it) } // 1, 2, 3 ``` The `flow { ... }` builder is a coroutine; `emit` sends a value downstream; `collect` is a `suspend` terminal operator that runs the stream.

  • Can you build a Flow from a fixed set of values without the flow { } builder?
    Yes — flowOf(1, 2, 3) or listOf(1,2,3).asFlow() create a Flow from known values.
  • Why can't a regular Sequence call a suspend function?
    Sequence's iterator runs synchronously on the calling thread; it has no coroutine context, so suspension points aren't allowed. Flow's builder is a coroutine, so it can suspend.

A List is a delivered package of all items; a Flow is a conveyor belt that hands you items one at a time as they're produced.

saying these in an interview costs you the question

  • Saying a Flow holds all its values in memory like a List
  • Claiming Flow blocks the thread while waiting for the next value
  • Confusing Flow with Sequence and saying both can suspend
  • Thinking Flow uses callbacks/listeners under the hood
  • Saying you must subscribe with a callback object like in RxJava

context