Kotlin asynchronous programming techniques compared

An introduction to Kotlin coroutines compared to Futures/Promises such as Reactor’s Mono or Java’s CompletableFuture

Danilo Barboza
The Startup
5 min readMay 9, 2020

--

Want to jump right into code? Go check it here.

Introduction

While reading about Kotlin's coroutines, I thought it would be really helpful to compare it to other async programming techniques, specifically the Futures/Promises approach that is widely used in Java.

Even though the Kotlin documentation is pretty good and comprehensive, I felt the lack of a more “real world” example. This is an attempt to fill that gap.

Do not expect to have a comprehensive view of how coroutines work nor how Java’s CompletableFuture or Reactor’s Mono work. The idea is to pick a quite simple made-up problem, implement it with the different approaches and libraries, and compare them. This is an introductory material to the topic.

Now let’s get into the made-up problem.

Problem Statement

Who never needed to work on a problem where you need to get data from one place, do some processing and send it to a different place? The made-up problem here is basically that.

Assume we work on e-commerce and we want to get the most recent order id from an HTTP endpoint, send this id to two other endpoints that:

  • Give us the stock information about the items in the order, and;
  • Give us the calculated delivery cost for the order.

Then we need to expose all that information combined to other consumers.

Let’s also assume that the operations after fetching the most recent order are slower on processing and we would like to run them in parallel.

We would like to implement something like the following:

Flow diagram

Futures/Promises approach

For the future/promises approach we will use two very popular APIs/libraries for dealing with Asynchronous Programming in Java: CompletableFuture and Mono.

CompletableFuture is available in the standard Java library since JDK 1.8 and got some improvements over the last releases. Because it is part of the standard library it is the default choice for Java libraries willing to support non-blocking processes and is used in some other APIs in standard libraries, like the java.net.HTTPClient.

Mono is the CompletableFuture equivalent in the Project Reactor library. Project Reactor is a quite popular non-blocking reactive library for the JVM. Its adoption has been growing recently, mostly because of its integration into Spring Framework. It has been adopted by other JVM libraries as well.

Now let’s see how the implementation works with these libraries. First, an implementation using CompletableFuture:

Now the Mono implementation:

They are quite similar, right? That’s exactly why they are here under the same section. They both follow the same async programming technique of Futures/Promises.

You can see that the only difference (apart from the return type) are the methods used, even though they have exactly the same mechanics:

  • thenCompose/flatMap are used to compose two different promises, meaning basically “after this promise is completed, return this other promise”;
  • thenCombine/zipWith are used to combine two different promises, meaning basically “give me a new promise that will complete after both promises are completed”.

We are not going into details of comparing both libraries, but, for basic use cases, you can see that they follow the same approach.

Coroutines approach

Kotlin coroutines are in essence lightweight threads. Kotlin language provides one high-level constructs, the suspending functions, and one library, the kotlinx.coroutines library, in order to deal with asynchronous programming in a way that is very similar to the regular imperative programming most of the developers are used to.

Think of it as the Kotlin way of doing async/await that is available in other languages such as C# or JavaScript.

Now, back to the topic. Here is how an implementation using suspending functions and constructs in kotlinx.coroutines library works:

You can see it seems very similar to the imperative programming style most of us are used to and is how we learn coding in the first place. A few notes:

  • All functions are suspending functions, as seen by the keywords suspend fun (lines 1, 13, 14). Suspending functions are basically the Kotlin way of coloring a function in order to advise the compiler that it may run asynchronous operation;
  • Suspending functions can only be executed from within another suspending function or a coroutineScope builder;
  • Line 3 is suspended, meaning execution will be suspended (non-blocking!) waiting for the return of fetchMostRecentOrderId which returns a String directly;
  • By using async with a suspending function, we can run it on the background, which returns a Deferred object (that is like a promise) as seen in lines 6 and 7;
  • Line 10 is suspended again until the result of both Deferred objects complete, by calling the await function on the Deferred objects;
  • We need to wrap the function into a coroutineScope in order to do parallel processing with async as coroutine code is executed sequentially by default.

Kotlin’s answer to async/await is slightly different from that on JavaScript. For example, here would be a similar function in JS:

The difference is subtle but important, while Kotlin coroutines are sequential by default, JavaScript ones are concurrent by default:

  • On line 2 we need to explicitly use await, while in Kotlin there’s no need to do so;
  • On lines 4 and 5 there’s no need to use any async construct, as async functions in JS returns a Promise.

Conclusion

This is quite a simple flow just to illustrate the differences in programming style/techniques.

One could argue that for this specific example, the code is cleaner and simple to understand using the Futures/Promise approach, and it would be a matter of style. However, as the code grows more complex and we have more and more requirements things can get quite hard to manage with Futures/Promise. You can check here for a good post with a more complex example comparing the two approaches.

Or even if we just get the same problem and just try to implement it sequentially things will get complicated with Futures/Promises. As seen here, we needed to introduce intermediate types in order to share context, in this case by using Kotlin Pair type.

I’ve also put together a simple Kotlin Spring Boot project where you can run the code yourself and play around with the different techniques discussed here.

This is a brief introduction to the topic and there is much more to get into the coroutines, for this, I would recommend the following material:

Thank you for reading until here and I hope you enjoyed it.

I would like to give a huge thank you to the friends and colleagues who provided their kind words and early feedback on this post. You all rock!

--

--