A coroutine is an instance of a suspendable computation: code that can pause while waiting and resume later without blocking its underlying thread. It is useful when an application must keep handling UI work, requests, or other tasks while network or I/O operations are pending. Coroutines are lightweight abstractions that run on JVM threads, so they support concurrent work without treating a thread as the unit of every short-lived task. section 14.0 section 14.3
Keep sequential logic non-blocking
Mark operations that may wait with suspend, then call them in normal top-to-bottom order. While login or loading data is suspended, the thread can perform other work instead of freezing a UI or wasting a request-handling thread. This readable shape is the practical advantage: asynchronous work does not force callback nesting or wrapped future return types. section 14.4.1 section 14.5
suspend fun processUser(credentials: Credentials) {
val user = authenticate(credentials) #1
val data = loadUserData(user) #2
val profilePicture = loadImage(data.imageID) #3
// ...
}
suspend fun authenticate(c: Credentials): User { /* ... */ } #4
suspend fun loadUserData(u: User): Data { /* ... */ }
suspend fun loadImage(id: Int): Image { /* ... */ }
The three calls read as ordinary sequential code, but each suspending operation may pause without blocking the thread. section 1.2.4
A suspending function is the core primitive. It may call another suspending function, but it must itself be called from a suspending function or from a coroutine. A coroutine supplies the machinery that lets its body suspend. Coroutine builders create these computations: use launch for work with no returned value, async for an asynchronously computed value, and runBlocking to bridge blocking code and suspending functions. section 14.6 section 14.9
suspend fun loadAndOverlay(first: String, second: String): Image =
coroutineScope {
val firstDeferred = async { loadImage(first) } #1
val secondDeferred = async { loadImage(second) } #2
combineImages(firstDeferred.await(), secondDeferred.await()) #3
}
async starts two image loads from one scope; await obtains both results before combining them. section 1.2.4
Coroutines are not merely renamed threads. Threads are comparatively expensive system resources and do not inherently form a task hierarchy. Structured concurrency gives coroutine work parent-child relationships for lifecycle management, cancellation, and errors. With ordinary non-supervisor parents, an uncaught failure in one child completes the parent exceptionally, cancels sibling children, and propagates the exception upward. This avoids continuing work that can no longer produce the shared result. section 14.3 section 18.2.1
Choose coroutines when the work benefits from these properties:
- Suspend during network requests or I/O so the underlying thread remains available for other work. section 14.3
- Keep dependent asynchronous steps in direct sequential-looking code rather than nested callbacks or chains of operators. section 14.4.1 section 14.5
- Start related concurrent tasks from a common scope so their lifetime, cancellation, and failures can be managed together. section 1.2.4 section 18.2.1
Coroutine support in the language and standard library is intentionally small; most higher-level APIs come from kotlinx.coroutines. Dispatchers select the thread or pool on which a coroutine runs, and multithreaded dispatchers require care when multiple coroutines modify shared data in parallel. Coroutines can provide concurrency and can also use the parallelism supplied by their underlying threads. section 14.6 section 14.9







