Kotlin Coroutines Intro
A coroutine is a piece of code that can pause its execution and resume later — without blocking the thread it runs on. Coroutines make asynchronous code look and behave like simple sequential code. They replace callbacks and complex threading setups with clean, readable logic.
The Problem Coroutines Solve
Without coroutines (blocking):
Thread 1: fetchData() ← thread is FROZEN while waiting for network
Thread 1: process() ← resumes after data arrives
If 1000 users do this: 1000 threads frozen → system runs out of threads!
With coroutines (non-blocking):
Coroutine: fetchData() ← coroutine PAUSES, thread is FREE to do other work
Other work runs on the same thread...
Network data arrives → coroutine RESUMES
Coroutine: process() ← continues from where it left off
Thread vs Coroutine
Thread:
┌─────────────────────────────────┐
│ Thread (heavy, ~1MB stack) │
│ Blocks while waiting │
│ OS manages switching │
└─────────────────────────────────┘
Coroutine:
┌─────────────────────────────────┐
│ Coroutine (lightweight, ~100B) │
│ Suspends (not blocks) │
│ Kotlin runtime manages │
└─────────────────────────────────┘
1 Thread can run thousands of coroutines
Adding Coroutines to Your Project
Add this dependency to your build.gradle.kts:
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
}Your First Coroutine
import kotlinx.coroutines.*
fun main() = runBlocking {
println("Start")
launch {
delay(1000) // suspends for 1 second (non-blocking)
println("Coroutine finished!")
}
println("Main continues while coroutine is waiting...")
delay(2000) // wait long enough to see the result
}Output:
Start
Main continues while coroutine is waiting...
Coroutine finished!Key Concepts
suspend function:
A function that CAN pause without blocking.
Can only be called from a coroutine or another suspend function.
Marked with the "suspend" keyword.
coroutine builder:
A function that STARTS a coroutine.
Examples: launch, async, runBlocking
coroutine scope:
The boundary that controls coroutine lifetime.
When scope ends, all its coroutines are cancelled.
dispatcher:
Decides which thread(s) a coroutine runs on.
Dispatchers.Main → UI thread (Android)
Dispatchers.IO → background I/O operations
Dispatchers.Default → CPU-heavy work
Coroutines Run Concurrently
import kotlinx.coroutines.*
fun main() = runBlocking {
val start = System.currentTimeMillis()
launch { delay(1000); println("Task A done") }
launch { delay(800); println("Task B done") }
launch { delay(600); println("Task C done") }
delay(1500) // wait for all to complete
val elapsed = System.currentTimeMillis() - start
println("All done in ${elapsed}ms")
}Output:
Task C done
Task B done
Task A done
All done in ~1500ms
Three tasks with total delay of 2.4 seconds finished in ~1.5 seconds because they ran concurrently on the same thread — not in sequence.
Coroutine Lifecycle
launch { ... }
│
▼
CREATED → ACTIVE
│
suspension point (delay, IO, etc.)
│
SUSPENDED
│
resume (data arrives, timer fires)
│
ACTIVE
│
block completes
│
COMPLETED
Practical Example: Simulated Parallel Downloads
import kotlinx.coroutines.*
suspend fun downloadFile(name: String, sizeKb: Int): String {
println("Starting download: $name")
delay(sizeKb.toLong() * 10) // simulate download time based on size
return "$name downloaded (${sizeKb}KB)"
}
fun main() = runBlocking {
val start = System.currentTimeMillis()
val r1 = async { downloadFile("report.pdf", 200) }
val r2 = async { downloadFile("image.png", 50) }
val r3 = async { downloadFile("data.csv", 100) }
println(r1.await())
println(r2.await())
println(r3.await())
println("Total time: ${System.currentTimeMillis() - start}ms")
}Output:
Starting download: report.pdf
Starting download: image.png
Starting download: data.csv
image.png downloaded (50KB)
data.csv downloaded (100KB)
report.pdf downloaded (200KB)
Total time: ~2100ms
