article banner

Flow from suspending functions and vice versa

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.