Kotlin Tutorial · Advanced Kotlin · Lesson 26 of 31

Kotlin Coroutines

Write asynchronous code that reads like sequential code with suspend functions, launch, async and structured concurrency.

Create a free account to save this page, take notes and track your progress. Sign in

suspend functions

A suspend function can pause without blocking its thread (for example while waiting for the network) and resume later. Coroutines come from the kotlinx.coroutines library (implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0")).

  • runBlocking bridges normal code and coroutines (used in main and tests).
  • launch starts a coroutine that returns nothing; async starts one that returns a Deferred you await.
  • delay suspends without blocking a thread.
async, await and launch
import kotlinx.coroutines.*
import kotlin.time.measureTime

suspend fun fetchUser(): String {
    delay(300)                      // pretend network call
    return "Ana"
}

suspend fun fetchOrders(): Int {
    delay(300)
    return 3
}

fun main() = runBlocking {
    val elapsed = measureTime {
        val user = async { fetchUser() }      // both start now
        val orders = async { fetchOrders() }
        println("${user.await()} has ${orders.await()} orders")
    }
    println(if (elapsed.inWholeMilliseconds < 550) "ran concurrently" else "ran sequentially")

    launch { println("background job done") }
    println("main continues")
}

Output

Ana has 3 orders
ran concurrently
main continues
background job done

Run this on your computer — see “Get Started” for setup.

Structured concurrency

Coroutines run inside a scope. A scope waits for its children, and if one child fails, the others are cancelled and the error reaches the caller: no leaked background work. withTimeout cancels slow work.

Structured concurrency and timeouts
import kotlinx.coroutines.*

suspend fun loadAll(): List<String> = coroutineScope {
    val a = async { delay(100); "profile" }
    val b = async { delay(50); throw IllegalStateException("orders service down") }
    listOf(a.await(), b.await())
}

fun main() = runBlocking {
    try {
        loadAll()
    } catch (e: IllegalStateException) {
        println("failed: ${e.message}; the profile request was cancelled too")
    }

    val result = withTimeoutOrNull(200) {
        delay(1_000)
        "slow result"
    }
    println(result ?: "timed out")
}

Output

failed: orders service down; the profile request was cancelled too
timed out

Run this on your computer — see “Get Started” for setup.

Test yourself

What happens to sibling coroutines when one child fails inside coroutineScope?

  1. They keep running
  2. They are cancelled and the exception propagates to the caller
  3. The program exits
  4. The failure is ignored
Show answer

Answer: They are cancelled and the exception propagates to the caller

Was this page helpful?