The Programmers Guide To Kotlin - Coroutines
Written by Mike James   
Monday, 07 June 2021
Article Index
The Programmers Guide To Kotlin - Coroutines
Launch
coroutineScope

Coroutines are now a stable part of Kotlin and an important way of creating asychronous code. Find out how they work in this extract from the second edition of my book on Kotlin for Programmers.

.

Programmer's Guide To Kotlin Third Edition

kotlin3e360

You can buy it from: Amazon

Contents

  1. What makes Kotlin Special
  2. The Basics:Variables,Primitive Types and Functions 
  3. Control
         Extract: If and When 
  4. Strings and Arrays
  5. The Class & The Object
  6. Inheritance
  7. The Type Hierarchy
  8. Generics
  9. Collections, Iterators, Sequences & Ranges
        Extract: Iterators & Sequences 
  10. Advanced functions 
  11. Anonymous, Lamdas & Inline Functions
  12. Data classes, enums and destructuring
        Extract: Destructuring 
  13. Exceptions, Annotations & Reflection
  14. Coroutines
        Extract: Coroutines 
  15. Working with Java
        Extract: Using Swing
  16. Compose Multiplatform
        Extract: Compose Layout ***NEW!

<ASIN:B0D8H4N8SK>

Most modern programs need to make use of asynchronous code in one form or another. The basic idea is that your program often can’t afford to wait around while some long-running task takes its time to complete. There are times when there is nothing else for the program to do, it just has to wait for a file to download or a computation to complete before moving on. However, many programs need to keep a User Interface (UI) active while something else happens or just get on with some other task while waiting.

The key idea behind asynchronous programming is that a function can be suspendable - it can be paused while something else happens and then restarted. A multitasking operating system can take advantage of this to switch between tasks to make it look as if there is more than one thing happening at a time. In addition, today multicore processors are almost the norm rather than the exception, and in this case the operating system really can run more than one thing at a time. Conceptually the difference between multithreading, i.e. switching between different threads of execution, and parallel processing i.e. running more than one thread of execution at the same time, is small.

Kotlin supports asynchronous programming by way of coroutines, which are not a new idea, but Kotlin’s take on them might give you pause for thought. Traditional ways of tackling the problem include callbacks and threads. Callbacks are simply functions passed to other functions which are called when the function completes. Threads are Java-based and you can use them in Kotlin in the same way. A coroutine is simply a function which can be suspended and restarted, but this hides a lot of the important detail. A single keyword, suspend, has been added to the language and the rest of the coroutine infrastructure is provided by a library which has been reimplemented on each of the platforms that you can run Kotlin.

In this chapter we are concentrating on the JVM implementation although the others, Android in particular, are very similar. Kotlin’s coroutines take a “structured” approach to running asynchronous code. This can seem strange at first if you are more familiar with threads but it has its advantages.

Coroutines are run on the same or a small number of threads. A dispatcher is used to share the thread between the different coroutines. This is a much less resource intensive way to implement asynchronous code and you can have thousands of coroutines running on a small number of threads. You can specify the type of dispatcher used for the thread by specifying its context, but this generally depends on the platform you are working with. For example, Android provides a range of contexts for I/O threads and so on.

This chapter is a brief introduction to coroutines and the associated technologies of flows and channels. This is not everything you need to know, but it is enough to make the documentation make sense.

Scope

The first idea you have to come to terms with is the scope of a coroutine. The biggest problem with this is the terminology. Scope is generally used to indicate where within a program a variable is accessible. In this case it refers to the lifetime of the coroutine rather than its direct accessibility. You can think of a CoroutineScope as being a container for coroutines, and consider the container’s lifetime to exist while all of the coroutines it contains are still active.

Let’s look at an example. The first scope you need to use is runBlocking. This accepts a function, which is nearly always a lambda function, as its final parameter. This means you can write the function that will be the coroutine as a trailing lambda, for example:

import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main() { println("main start")
runBlocking {
println("Coroutine1 start")
for (i in 1..20) {
print(i)
}
println(" Coroutine1 finishing")
}
println("Main stopped")
}

To make this work you also need to add to the build.gradle file:

dependencies {
    implementation("org.jetbrains.kotlinx:
kotlinx-coroutines-core-jvm:1.5.0") }

If you run the program you will see:

main start 
Coroutine1 start
1234567891011121314151617181920 Coroutine1 finishing
Main stopped

This is hardly impressive, but the main function was paused by the call to runBlocking which, as its name suggests, is a blocking call. The lambda passed to the runBlocking function runs while the main program is paused and when it comes to an end the runBlocking returns and the main program resumes. To be more precise, the thread that is running main is used by runBlocking to start a dispatcher which then runs any coroutines created inside it.

Of course, all of this looks exactly like what would happen if main just called the lambda function, but what is happening is very different.

The runBlocking converted the lambda to a coroutine and placed it in the queue of the Main dispatcher. As there is nothing else in the dispatcher’s queue, the lambda runs at once and when if finishes the queue is empty and the main program thread is restarted.

Notice that a disadvantage of using runBlocking is that it uses a dispatcher with just the Main thread to allocate. This is sufficient for us to investigate some of the aspects of coroutines, but you need to keep in mind that dispatchers that use multiple threads also exist, see later.

kotlin2e180



Last Updated ( Monday, 07 June 2021 )