Writing asynchronous code is hard, even when we have amazing tools such as Reactive Programming to help us. What if we could write synchronous code and have it work asynchronously?

On the Android Team at The New York Times, we tackle writing asynchronous code with help from our Store library and from RxJava, which is a fundamental piece of our architecture. We use it to handle our most computationally intensive tasks, from parsing the news feed to reacting to UI changes.

Although we have used RxJava up to this point, we also try to keep ourselves and our app up-to-date, so we have started to incorporate the use of coroutines. Coroutines were introduced only recently as an experimental feature of Kotlin 1.1 and they give developers the ability to write more concise, asynchronous code. Even if coroutines are not a new concept (they exist in multiple other languages), it is really amazing that they are available in Kotlin and on Android, where the features of Java 8 have been a dream for several years.

In this post, we will walk through some examples of coroutines and talk about what is going on under the surface.

Blocking Vs Non-Blocking

Before we learn how we can make use of coroutines, we have to understand the difference between blocking calls and non-blocking calls. Let’s look at the following snippet:

launch {

delay(1000L)

println("World!")

}

println("Hello,")

Thread.sleep(2000L)

What we have here is pretty straightforward, but it has some peculiarities: first, we have not one but two different ways of invoking a waiting time. Something we may be familiar with ( Thread.sleep() ) and something we might have instead seen within RxJava’s operators ( delay() ). Invoking sleep() is a blocking call, while delay() is a non-blocking call. Diving deep, the first call will make the whole thread sleep, freezing its execution for as long as we request, while delay() will only suspend it, allowing the rest of the code to work normally.

Let me explain this even further: when we launch this routine, it will encounter delay() , which will make the code between launch and the closing bracket suspend for one second, while the rest (the println instruction) will be executed. This is when we see the output in the console printing “Hello,”. The next step is Thread.sleep() , which will freeze the execution for two seconds, blocking everything that was in the pipeline, which means that the delay() counter will freeze as well. When this time passes, delay() will restart and finish, at which point we’ll see the last output in the console printing “World”.

Visualizing this as a timeline can maybe help make this very important concept crystal clear:

start[delay(1s)] -> println(“World!”) will wait for its execution time

println(“Hello,”) -> this will be printed immediately

Thread.sleep(2s) -> everything will freeze for 2 seconds

finish[delay(1s)]

println(“World!”) -> will be printed when both Thread.sleep() and delay() are over

This concept is so important because the definition of coroutines is:

Coroutines are computations that can be suspended without blocking a thread.

Now that we know this, we can easily identify this snippet as our first coroutine:

launch {

delay(1000L)

println("World!")

}

Before discussing more theory, let’s see why delay() is so important. For coroutines to be suspendable without blocking everything else, we have to give up a little bit of flexibility, meaning that we can only stop them at specific points; and delay() is one of them. These points (which are functions, after all) are called suspension functions, and are created by applying the suspend modifier to the declaration of the function itself:

suspend fun delay(

time: Long,

unit: TimeUnit) {

…

}

Another very peculiar aspect is that suspending functions can only be called inside a coroutine or from another suspending function.

But how can we create a coroutine, then? The answer is by using a builder. A builder will help us create a coroutine by accepting suspending functions as parameters. In our previous example, the builder is identifiable with the launch function, which creates a fire and forget coroutine that will not return any value when its execution has completed.

At this point, we can finally identify the launch() function: in Kotlin, this is called builder, and it is what makes the coroutine possible. Here is one possible declaration of a launch() function:

fun launch(block: suspend () -> Unit): Job

As we can see, it’s nothing more than a higher-order function. Together with suspending functions, they provide us with a powerful way to write asynchronous code clearly and concisely.

Illustration by Elena Xausa

Project setup

As of the time of writing, coroutines are still experimental and cannot be used unless we take a few introductory steps. The first thing we have to do is open the gradle.properties file in our project and add this line, which will enable coroutine support in the IDE.

kotlin.coroutines=enable

The next step is adding their dependency, which should look very similar to this line. We add this to our module’s build.gradle file:

dependencies {

…

compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.5"

}

The last step we have to take is enabling the coroutines for our build and we do it by adding this snippet to the same build.gradle file in which we have been operating so far:

kotlin {

experimental {

coroutines "enable"

}

}

An easy example

Suppose we want to create a very simple countdown function, which will change the value in a TextView from ten down to one and will then replace the number with “Done!” as soon as we have finished counting. To make it slightly more complex, at each iteration of the countdown we want to wait for one second, so that the timing is as close as possible to reality. Since we want to use coroutines, what we have to do is use the launch() builder and insert the delay() function in a simple for loop, in which we also change the label of a TextView .

launch(UI) {

for (i in 10 downTo 1) {

hello.text = "Countdown $i…"

delay(1000)

}

hello.text="Done!"

}

The only odd thing here is that parameter we passed to the launch() builder: this optional parameter is the coroutine context (not related to Android’s object with the same name) which will instruct the coroutine on where we want this code to be executed. In this case, we want it to be run on the UI Context , which is a wrapper of Android’s Main Looper. Since we are going to invoke methods of a View , we can only do so from the thread which created the aforementioned View . We should worry not: as we already saw, a suspending function will only suspend the coroutine, without impacting our main thread, so we are sure that delay() will not make our app unresponsive.

In the case that we want to cancel this countdown, we can do so by just holding a reference to the Job returned by launch() (as we saw when we introduced launch() declaration, this builder function actually returns a Job object) and invoking the cancel() method on it.

val job = launch { … }

job.cancel()

Note: All coroutines should be cancelable, but we can also make a noncancelable coroutine. However, this discussion is out of the scope of this introduction.

Let’s take this example a bit further and refactor it so that we can more clearly understand how coroutines can help us in our everyday life.

We can start by wrapping the countdown routine into an extension function so that we can invoke it on any of the TextViews in our layouts:

fun TextView.countdown() {

for (i in 10 downTo 1) {

text = "Countdown $i…"

delay(1000)

}

text="Done!"

}

Since delay() is a suspending function, and suspending functions can only be called from inside other suspending functions, we mark our countdown() extension as suspending as well. The downside of this can be that we will not be able to call countdown() whenever we want, but since we need to suspend the execution, this is a fair limitation that we are willing to accept.

suspend fun TextView.countdown() {

for (i in 10 downTo 1) {

text = "Countdown $i…"

delay(1000)

}

text="Done!"

}

At this point, we want this routine to be fired when we press a button, so that we can start the countdown whenever the user wants to. To do so, we need to wrap a builder function into a View.OnClickListener . The quickest way to reach this goal is to leverage the Kotlin language and create an asynchronous click listener.

fun View.onClickAsync(action: suspend () -> Unit) {

setOnClickListener {

launch(UI) {

action()

}

}

}

This snippet shows nothing more than what we mentioned above: we create a higher-order function, a parameter of which will be a suspending function, and we wrap our coroutine in an OnClickListener , invoking the suspending function we received as parameter.

Now, we just need to bind this logic to a View and a TextView , so that we can see the magic happen:

fab.onClickAsync { textView.countdown() }

We can take this example a bit further, maybe with a network call, to demonstrate how easy and readable coroutines can make our code. Let’s use the Retrofit framework which, at the time of writing, has no official support for this feature. However a pull request has been issued and we should see this compatibility layer added sooner rather than later. Before we start diving into the code, we have to highlight a couple of other small things: We only encountered the launch() builder, but it’s not the only one. Its duty is to create a ‘fire and forget’ coroutine, which will not return any result; while its counterpart, called async() , will return a value which we can literally await.

Let’s only focus to the relevant part of the code. Since coroutines have not yet a stable AdapterFactory for Retrofit, we can either create one or we can go for a different (and maybe less elegant) solution, which is having the framework perform synchronous calls that we’ll make asynchronous later.

In order to do this, we instruct our interface to return a Call<OurType> :

interface NetworkService {

@GET(“whatever”)

fun getData() : Call<OurType>

}

The next step is to wrap this call in a function which will only return OurType , keeping the code as simple as possible:

fun getDataSync() = weatherService.getData().execute().body()

Note: If we try and run this snippet as it is, we will incur in a NetworkOnMainThreadException which, in our example, means we are doing exactly what we wanted to do, that is make a synchronous network call with Retrofit.

Now we can finally introduce async() . Differently from what we saw with launch() , this function returns a Deferred<T> (in our case, T is OurType ), which is an object that holds the promise that we will have a value, when the coroutine ends its duty. Of course, we have to create a suspending function, as we already saw several times:

suspend fun getDataAsync() =

async {

getDataSync()

}

Now, we can wire this function together with the asynchronous click listener we already saw, and when we get our data, we just pass it to our Views in the correct thread, while executing the network call away from the main thread:

fun View.onClickAsync(action: suspend () -> Deferred<OurType>) {

setOnClickListener {

launch(UI) {

val response = action().await()

bindUiTo(response)

}

}

}

Here, we changed the function we expect as parameter to match the fact that we will return a value, but the interesting thing is when we want to obtain a value from a Deferred<T> object, we invoke the await() method, which will return the T value whenever it is ready. To recap, what we do here is ask for a value which will be computed on another thread, and we await for the result, which we will obtain on the UI thread, so that we can use it right away.

Illustration by Elena Xausa

Coroutines are by default scheduled for execution as soon as we create them, so that we don’t have to wait as long for them to complete. In case we need them to start sequentially, maybe because they depend on each other’s result, we can make them start lazily, and they will be executed as soon as we invoke the await() method on a Deferred<T> instance.

To make them lazy, we simply pass another optional parameter to the builder, which will instruct them on when to start:

async(start = CoroutineStart.LAZY) {…}

Testing

At The New York Times, we really like to test the code we write, so we definitely could not conclude an article without mentioning how we can test a coroutine. One way is to force our asynchronous routine to run synchronously so that our tests can run in a very predictable way. To do this, we use yet another builder, which is runBlocking() . This builder forces our code to run in a blocking way so that we can always predict the behavior of the test:

@Test

fun coroutine_test() {

val actualWeather = runBlocking {

getDataAsync().await()

}

…

}

Note: This test is an example of how to test a coroutine. We are specifically not mentioning the different ways to test our network layer.

Coroutines are experimental but they are stable enough for daily use and the incoming support for all the major frameworks and libraries make it clear that they are here to stay. They make writing asynchronous code cleaner and easier, and we are quite happy to have them.