Characteristics, Elements, and Real-life Use Cases of Flow
Learn about the characteristics and elements of flow using real-life examples.
The characteristics of Flow
The terminal operations of Flow (like collect) suspend a coroutine instead of blocking a thread. They also support other coroutine functionalities, such as respecting the coroutine context and handling exceptions. We can cancel flow processing, and structured concurrency is supported out of the box. The flow builder is not suspending and does not require any scope. The terminal operation suspends and builds a relation to its parent coroutine (similar to the coroutineScope function).
Example
The example below shows how the CoroutineName context is passed from collect to the lambda expression in the flow builder. It also indicates that launch cancellation also leads to proper flow processing cancellation.
package kotlinx.coroutines.app
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
// Notice, that this function is not suspending
// and does not need CoroutineScope
fun usersFlow(): Flow<String> = flow {
repeat(3) {
delay(1000)
val ctx = currentCoroutineContext()
val name = ctx[CoroutineName]?.name
emit("User$it in $name")
}
}
suspend fun main() {
val users = usersFlow()
withContext(CoroutineName("Name")) {
val job = launch {
// collect is suspending
users.collect { println(it) }
}
launch {
delay(2100)
println("I got enough")
job.cancel()
}
}
}Flow nomenclature
Every flow consists of a few elements:
- The flow needs to start somewhere. It often starts with a
flowbuilder, conversion from a different object, or helper function. We will explain an essential option in the next chapter, “