liveBook · Manning

Upstream flow

As explained in Kotlin in Action, 2E

An upstream flow is the portion of a flow, including preceding intermediate operators, that occurs before a specified operator.

An upstream flow is the flow an intermediate operator receives. The operator returns a downstream flow, which can become the upstream flow for the next operator. Intermediate operators remain cold until a terminal operator such as collect runs. section 17.2 section 17.5

Reading an operator chain

flowOf(1) .map { it + 1 } .filter { it > 1 } .onEach { println(it) } .collect() Here, flowOf(1) is upstream of map; map's result is upstream of filter; and onEach is upstream of collect. None of the operators executes until collect is invoked. section 17.2

The position of an operator determines which part of the pipeline it affects:

  • flowOn changes the coroutine context of its upstream flow, including intermediate operators before it; downstream operators remain unaffected. section 17.2.7
  • A custom intermediate operator collects its upstream flow inside a flow builder and emits transformed values downstream, preserving cold execution. section 17.3

Custom upstream processing

fun Flow<Double>.averageOfLast(n: Int): Flow<Double> = flow { val numbers = mutableListOf<Double>() collect { if (numbers.size >= n) numbers.removeFirst() numbers.add(it) emit(numbers.average()) } } Calling averageOfLast on a flow makes that flow the upstream input; collecting the returned flow triggers the processing.

Figure 17.2 The terms upstream and downstream describe where an intermediate operator sits in the operator chain in relation to another operator. The flow that an operator is being called on is the upstream flow. The flow it returns is the downstream flow. In this example, flow is upstream of map, filter is downstream of map, onEach is upstream of collect, and so on.
CH17_F02_Isakova

Use the operator-chain figure to trace how upstream and downstream positions change across map, filter, onEach, and collect. section 17.2

How Kotlin in Action, 2E covers it

How deep it goes
Defines upstream/downstream relationships, demonstrates cold execution, explains flowOn boundaries, and sketches custom-operator internals.
Examples
Uses map, filter, onEach, collect, flowOn dispatcher switches, and a rolling averageOfLast custom operator.
What it assumes
Assumes familiarity with flows, intermediate operators, collect, flow builders, and coroutine dispatchers.
What you can do after
Apply it via Kotlin Flow operators, predict flowOn scope, and build cold custom operators that collect upstream values and emit downstream results.
How it connects
Connects upstream flow with intermediate operators, downstream flow, terminal operators, cold flows, coroutine contexts, and custom operators.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Kotlin in Action, 2E. The section links point to the parts of the book it draws on.