Kotlin Flows and Coroutines

Roman Elizarov
May 11, 2019 · 4 min read
Image for post
Image for post
Channels by Tom Doel

In the previous story on Kotlin Flows I’ve shown how they are designed¹ and one thing was missing from that description on purpose — there was no mention of either coroutines or channels. Indeed, the design of Kotlin Flows is based on suspending functions and they are completely sequential, while a coroutine is an instance of computation that, like a thread, can run concurrently with the other code.

Sequential flows

Let us confirm that collecting it takes around a second:

But what happens if the collector is also slow and adds its own 100 ms delay before printing each element? Check it out:

It takes around 2 seconds to complete, because both emitter and collector are parts of a sequential execution here and it alternates between them:

Image for post
Image for post

Concurrent coroutines

Image for post
Image for post

This is a common communication pattern and it can be encapsulated into an operator on flows. Build-in produce builder from kotlinx.coroutines library makes this pattern especially easy to implement, since it combines launching a new coroutine and creating a channel, and consumeEach function pairs with it on the consumer side. We use coroutineScope so that concurrency is structured³ and to avoid leaking the producer coroutine outside of the scope:

Running the same emitter and collector code as before but with the above buffer() operator in between them gives the desired speedup in execution time:

Declarative programming

In fact, the recent academic research corroborates this anecdotic knowledge:

Contrary to the common belief that message passing is less error-prone, more blocking bugs in our studied Go applications are caused by wrong message passing than by wrong shared memory protection.

On the other hand, programming with flows is declarative. Instead of manually launching coroutines and setting up channels between them, the corresponding patterns will be written and debugged once, and encapsulated into operators on flows.

Roadmap

I’m getting one question quite often: “Are channels going to be deprecated when the flows are finalized?”. The purpose of this story was to show why the answer is a definite “No”. The channels are going to be used as a low-level implementation mechanism on which various flow operators, requiring communication between coroutines, will be based.


Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store