Kotlin Coroutines in Android — Part 4
Running coroutines sequentially or in parallel
One of the main advantages of asynchronous programming is that it allows to offload computations to separate threads and run them in parallel in case the result of each task is not needed by the others. They can all run independently making use of multiple CPU cores. We can achieve the same behaviour also with coroutines. Let’s take a look at how to run them sequentially or in parallel depending on our needs.
Running coroutines sequentially
Running tasks sequentially means that we want to wait for each one to complete before moving to the next one. This is needed for example if the result of one task is used by the next one. For this purpose, withContext() is the ideal solution.
val result1: Int = withContext(Dispatchers.Default) {
(1) // ... do something ...
return@withContext value1
}val result2: Int = withContext(Dispatchers.Default) {
(2) // ... do something with result1 ...
return@withContext value2
}... other code in the parent coroutine ...
(1) is executed first and the first withContext() doesn’t return until (1) completes. When (1) completes, we have the result in result1 and the second withContext() is executed. (2) can use the value available in result1 if needed. Again, given that withContext() is a suspend function, the parent coroutine is suspended until (2) completes. At that point, result2 will be available within the parent coroutine to continue with other operations.
Running coroutines in parallel
Running tasks in parallel means that they can run at the same time on separate threads making use of the multiple CPU cores available. This results in faster processing time because we don’t need to wait for one task to complete before moving to the next one. This is typically possible when the result of one task is not used by the next one. A good candidate to achieve this behaviour in the coroutines world is for sure async().
val result1Deferred: Deferred<Int> = async(Dispatchers.Default) {
(1) // ... do something ...
return@async value1
}val result2Deferred: Deferred<Int> = async(Dispatchers.Default) {
(2) // ... do something ...
return@async value2
}val result1: Int = result1Deferred.await()
val result2: Int = result2Deferred.await()... other code in the parent coroutine ...
(1) is executed in parallel with (2) because whenever async() is called, it returns immediately without suspending the parent coroutine. When we call await() on the Deferred objects returned by async(), we are actually suspending the parent coroutine and waiting for (1) and (2) to complete because we need their results before continuing the execution of the parent coroutine.
What’s next
So far, we’ve seen how to run coroutines, but we haven’t seen yet how to cancel them in case their execution is not required anymore and we don’t want to wait for them to complete. This will be the topic of the next part.
Get the source code
The source code for this series can be found on GitHub. It’s an example Android project that covers multiple cases. Download it and play with it.
Missed the other parts of this series?
If you’ve missed the other parts of the Kotlin Coroutines in Android series, take a look at the introduction and check the full list of topics.