Coroutines are not something new — they have been researched in academia since the 1960s. They are slowly becoming more widely used in industry in languages such as C#, python, Lua and just recently, Kotlin 1.3 as of October 2018!
In the official docs, Kotlin coroutines are described as “light-weight threads”. It was a little confusing to me at first because I learned coroutines as, functions that can be suspended at some point and resumed from that point later on.
Suppose we have two functions, Function A and Function B that execute one after the other. Let Function A have a blocking call such as a network request or doing some sort of I/O. This will block the calling thread. The thread can then execute Function B only after Function A is complete.
What if function A takes a long time to complete? That’s a lot of wasted time that the thread can be used to do other useful work. If only there was a better way to fully utilize the thread..
Fortunately the answer is coroutines! Coroutines allow the calling thread to pause execution during Function A and work on other tasks such as Function B in the meantime.
Suspend Functions
Coroutines are functions that have the suspend keyword.
suspend fun method() { ... }
Suspend functions are the primitives upon which the Coroutines library is built.
- One can pause and resume thread execution within a suspend function
- Only a suspend function can be called within another suspend function.
- A key thing to note is that calling a suspend method does not block the calling thread.
Here’s a simple example of a coroutine.
The code is launching three coroutines, each running on the main thread. The runBlocking wrapper is to prevent the main program thread from terminating until all coroutines have finished. Delay is a function to pause the current coroutine for a period of time. You can see that the three run() methods aren’t executed sequentially, but rather being switched back and forth. This is because the main thread can suspend and resume execution in each coroutine. This can be quite useful in Android where the main UI thread can switch between doing different tasks without ever being blocked.
Here is a usage of a Retrofit client that uses coroutines to make non-blocking HTTP calls. This allows the caller thread (main thread) to do other work in the meantime.
To see other ways to launch a coroutine and the different coroutine contexts, here is a cheatsheet for coroutines that I found helpful.
Preserving State
Since coroutines allow you to suspend and resume program execution, we can use them to preserve state. For example, let’s say that we wanted to create a class that prints out the Fibonacci series. This class will have only one method and each time the method is called, it outputs the next Fibonacci number in the series. There are many ways to implement this, but you might notice that we may need to store a lot of class variables such as the previous, current and next Fibonacci numbers. However, what if we didn’t need to have these global variables and keep track of their states?
See how we don’t need to track global state anymore? With a simple program like Fibonacci, tracking global state isn’t a difficult thing to do. However, if this were a more complex program with more states, then reducing the number of global variables may reduce the complexity of the code significantly.
Clean Asynchronous API
Coroutines allow you to write clean asynchronous code.
fun main(args: Array<String>) = runBlocking {
val first = async { calculate1() }
val second = async { calculate2() }
val third = async { calculate3() } val result = first.await() + second.await() + third.await()
}
This is much cleaner than Java’s CompletableFuture result chaining and most importantly, no callbacks…
Calling async launches the coroutine in another thread. This brings us back to Kotlin team’s definition of Coroutines as lightweight threads. The reason they are lightweight is because they are actually cheaper than starting a thread. Coroutines run on a thread pool behind the scenes and they have no magic inside. “You can always manually write the same code using your own thread pool and get the same performance. The value of coroutines is that they let you write nicer, shorter, easier to read code.”
In conclusion
Coroutines are functions that can be suspended at some point and resumed from that point later on. Suspend functions are the building blocks of Kotlin coroutines and we need to properly understand them to build higher level abstractions on top of coroutines. This is just scratching the surface of what coroutines can do.
Very helpful resources
I really recommend watching the Kotlin coroutines videos by Roman Elizarov.