Coroutines Basics
|
This section documents Kotlin 2.4.x on the JVM, as published at kotlinlang.org, which is the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against kotlinlang.org before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Coroutines are Kotlin’s answer to asynchronous programming — code that reads top-to-bottom like ordinary
sequential code, but can suspend without blocking the underlying thread. They ship as a separate library,
kotlinx.coroutines, not the language core itself, though the suspend keyword that makes them possible is a
language feature.
suspend Functions
A function marked suspend may pause its execution at a well-defined point (e.g. while awaiting an I/O result)
and resume later, without blocking the thread it started on — the compiler transforms it into a
state-machine-like implementation behind the scenes:
suspend fun fetchUser(id: Int): User {
delay(100) // "delay" suspends, unlike Thread.sleep which blocks the thread
return httpClient.get("/users/$id").parseAsUser()
}
A suspend function can only be called from another suspend function, or from inside a coroutine — this
"coloring" is deliberate: it makes it visible at every call site whether a function can suspend, the same way
Kotlin makes nullability visible in a type rather than leaving it implicit.
Coroutine Builders: launch, async, runBlocking
A coroutine is started by a builder function, each suited to a different situation:
| Builder | Use |
|---|---|
|
fire-and-forget — starts a coroutine and returns a |
|
starts a coroutine that does produce a result, returned as a |
|
bridges regular blocking code into the coroutine world by blocking the current thread until its coroutine
body completes — almost always confined to a |
fun main() = runBlocking { // bridges "main" (not a suspend function) into coroutines
launch { // fire-and-forget child coroutine
delay(100)
println("launched work done")
}
val deferred = async { // produces a result
delay(50)
42
}
println("the answer is ${deferred.await()}") // suspends here until "async" completes
println("main continues immediately after starting the coroutines above")
}
CoroutineScope
Every coroutine builder is an extension function on CoroutineScope — a coroutine can only be launched
within a scope, and that scope is what ties the coroutine’s lifetime to something meaningful (a screen, a
request, a test):
class UserRepository(private val scope: CoroutineScope) {
fun refreshInBackground() {
scope.launch {
val user = fetchUser(1)
println("refreshed: $user")
}
}
}
On Android, viewModelScope and lifecycleScope (see
Kotlin for Android) are the ready-made scopes tied to a
ViewModel’s or a `Lifecycle-aware component’s own lifetime, so coroutines are cancelled automatically when
the screen goes away.
Structured Concurrency
Structured concurrency means a coroutine started inside a scope becomes a child of that scope, and the parent will not consider itself "done" until every child has finished — cancelling a parent automatically cancels every descendant, and an unhandled exception in a child propagates up to the parent by default. This is what prevents the classic "fire-and-forget coroutine that outlives its owner and leaks" bug a manually-managed thread pool is prone to:
suspend fun loadDashboard() = coroutineScope { // creates a scope tied to THIS suspend call
val user = async { fetchUser(1) } // child 1
val orders = async { fetchOrders(1) } // child 2
Dashboard(user.await(), orders.await()) // suspends until BOTH children complete
// coroutineScope itself doesn't return until every child (including any it doesn't wait on
// explicitly) has completed or been cancelled
}
See Also
-
Coroutine Context, Cancellation and Exceptions —
Dispatchers, timeouts, andSupervisorJobfor opting out of default failure propagation. -
Flows — coroutine-based streams of multiple values over time.
-
Kotlin for Android —
viewModelScope/lifecycleScopein practice.