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.
Use the operator-chain figure to trace how upstream and downstream positions change across map, filter, onEach, and collect. section 17.2
