Kotlin Coroutines: An Introduction

Kotlin Coroutine Introduction
Get started with Coroutines and their implementation in Kotlin using the kotlinx.coroutines library: suspending functions, Coroutine scopes and contexts

Introduction

What are coroutines

Coroutines are a simple way to implement asynchronous and non-blocking code. They rely on functions that have the ability to suspend their execution at some point and resume later on: suspending functions. They cooperatively take turn to execute, thus enabling a non-blocking style of programming.

Compared to threads, they are:

  • Light-weight and don't require a lot of system resources: one thread can run many coroutines.
  • Safer and less error-prone.

Coroutines in Kotlin

Kotlin only provides support to suspending functions at the language level, and relies on libraries to implement high-level coroutine-enabled primitives.

kotlinx.coroutines is the primary coroutine library for Kotlin.

A coroutine can be launched by invoking a coroutine builder inside a coroutine scope (context).

import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch { // launch a new coroutine
        delay(500L)
        println("World!")
    }
    println("Hello")
    runBlocking {
        delay(1000L)
    }
}
  • A coroutine scope is an entity responsible for controlling the lifecycle of a group of coroutines and their context. Scopes wait for all their associated coroutines to complete before completing themselves.
  • Coroutine builders are extension functions of coroutine scopes. They launch new coroutine and return either a Job (ie: launch) or a Deferred value that will hold a future result (ie: async).

Suspending functions are marked with the suspend modifier in Kotlin. They can be used inside coroutines just like regular functions, but their additional feature is that they can, in turn, use other suspending functions such as those provided by the standard library (ie: delay).

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch { printWorld() }
    println("Hello")
}

suspend fun printWorld() {
    delay(1000L)
    println("World!")
}

For more detailed Kotlin Coroutine examples, you can follow this practical guide.

Additional references

Coroutine scopes and contexts

Coroutine scopes are used to defines new coroutines. To launch top-level coroutines which are operating on the whole application lifetime and are not cancelled prematurely, GlobalScope can be used.

New standalone coroutine scopes can be created:

  • using factory functions such as CoroutineScope and MainScope.
  • with scope builders such as coroutineScope and withContext. In addition to defining a new coroutine scope, they launch a given suspending block of code within the defined scope. runBlocking can be used there is a need to block the current thread while waiting for the completion of the block of code.

Contexts

A Coroutine context is a set of elements that defines a Coroutine execution behavior.

They can be associated to coroutines scopes by:

  • passing them as constructor arguments while creating new scopes.
  • appending them to existing scopes using the plus + operator.

CoroutineDispatcher is a context used to specify the kind of thread where the coroutine should be executed:

  • Dispatchers.IO can be used to offload blocking IO tasks to a shared pool of threads
  • Dispatchers.Main is used to run tasks that should be execute in a main UI thread.
  • The Default dispatcher is used when no ContinuationInterceptor is associated to a scope.

Job is a context element that can be used to start, cancel and wait for the completeness of a Coroutine.

Additional references


GlobalScope launch
Simple coroutine scope with GlobalScope
coroutineScope + withContext
coroutineScope and withContext usage examples
CoroutineScope with plus operator
Coroutine context merge with + operator

Soufiane Sakhi is an AWS Certified Solutions Architect – Associate and a professional full stack developer.