A flow builder is the Kotlin coroutines function flow, which creates a cold Flow from a suspending block. Call emit inside the block to offer values over time; suspending functions such as delay can run between emissions. Creating the Flow performs no work until a terminal operator, usually collect, starts it. section 16.2.1 section 16.1.1
Emit values with a delay
A function can return Flow<String> from ordinary, non-suspending Kotlin code while performing suspending work inside the builder. Collecting letters starts the block, emits A, waits 200 milliseconds, and emits B. Values become available as they are emitted rather than after the entire sequence is computed. section 16.2.1 section 16.1.1
Listing 16.3 Calling suspending functions from the flow builder
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlin.time.Duration.Companion.milliseconds
fun main() {
val letters = flow {
log("Emitting A!")
emit("A") #1
delay(200.milliseconds)
log("Emitting B!")
emit("B")
}
}
Use the compact Kotlin listing to see emit and delay together inside a flow builder. section 16.2.1
Keep these execution rules in mind:
- Treat the returned Flow as inert until collection begins; collecting the same cold flow again executes its builder again, including side effects such as network requests. section 16.2.2
- Understand emit as an asynchronous return: it invokes the collector and suspends the builder until that collector finishes processing the value. section 16.2.1 section 16.2.4
- Build custom intermediate operators by creating a flow, collecting the upstream flow inside it, transforming each element, and emitting downstream values. section 17.3
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())
}
}
Use the custom averageOfLast operator to trace how a flow builder remains cold while collecting upstream and emitting transformed results. section 17.3
