Suspending functions are just functions that can suspend the coroutine that calls them. They are typically used to represent tasks that might require suspension, like network requests or database operations (coroutine gets suspended when until the result is available). Suspending functions can internally start asynchronous processes, but they typically await their completion before returning a result.
Flow is used as a definition of a stream of values. When we callect it, we often start some asynchronous process that produces values one by one. Flow is typically used to represent situations where we might receive from zero to many values, like a stream of events, a stream of database changes, or a stream of messages on a websocket.
Those are two different abstractions, but since they are often used together, we sometimes need to convert between them. In this article, I will show you how to do that.
Turning suspending functions into Flow
The simplest way to turn a suspending function looks as follows:
fun userFlow() = flow {
emit(getUser()) // getUser is a suspending function
}
Here we define a Flow builder, that once started, makes a call to a suspending function getUser and emits its result. This simple pattern is often extracted into a top-level:
// Also named singleValueFlow, flowOfSingle, etc.
fun <T> flowOf(builder: suspend () -> T): Flow<T> = flow {
emit(builder())
}
If you have suspending function type suspend () -> T (or even regular function type () -> T), you can turn it into a Flow using asFlow() extension function:
val f: suspend () -> User = suspend { getUser() } // suspending function
val userFlow: Flow<User> = f.asFlow() // converts it to Flow
val f2: () -> User = { createUser() } // regular function
val userFlow2: Flow<User> = f2.asFlow() // converts it to Flow
Turning Flow into suspending functions
A flow can contain many values spread over time, so it is much more flexible than a suspending function. If we want to turn is into a suspending call, we need to decide what do we want to loose. We can use:
firstOrNull() that suspends until the first value is emitted, then returns it or null if flow completes without emitting any value.
lastOrNull() that suspends until this flow completes, then returns the last value emitted or null if flow completes without emitting any value.
toList() that suspends until this flow completes, then returns a list of all values emitted by this flow.
suspend fun main(): Unit = coroutineScope {
val alphabetFlow: Flow<Char> = flow {
for (c in 'A'..'Z') {
delay(1000) // Simulate some delay
emit(c) // Emit each character
}
}
launch {
// Receives A after 1 second
val firstChar: Char? = alphabetFlow.firstOrNull()
println("First character: $firstChar")
}
launch {
// Receives Z after 26 seconds
val lastChar: Char? = alphabetFlow.lastOrNull()
println("Last character: $lastChar")
}
launch {
// Receives a list with all characters after 26 seconds
val allChars: List<Char> = alphabetFlow.toList()
println("All characters: $allChars")
}
}
Conclusion
In this article, I showed you how to convert between suspending functions and Flow. This is a common task in Kotlin Coroutines, as we often need to work with both abstractions. Remember that suspending functions are typically used for single values, while Flow is used for streams of values. Use the provided patterns to convert between them when needed.
Marcin Moskala is a highly experienced developer and Kotlin instructor as the founder of Kt. Academy, an official JetBrains partner specializing in Kotlin training, Google Developers Expert, known for his significant contributions to the Kotlin community. Moskala is the author of several widely recognized books, including "Effective Kotlin," "Kotlin Coroutines," "Functional Kotlin," "Advanced Kotlin," "Kotlin Essentials," and "Android Development with Kotlin."
Beyond his literary achievements, Moskala is the author of the largest Medium publication dedicated to Kotlin. As a respected speaker, he has been invited to share his insights at numerous programming conferences, including events such as Droidcon and the prestigious Kotlin Conf, the premier conference dedicated to the Kotlin programming language.