Skip to main content

Coroutines

Trushi Jasani
EditReport

Coroutines

What are Coroutines?โ€‹

Coroutines are Kotlin's approach to asynchronous, non-blocking programming. They are lightweight threads that can be suspended and resumed, allowing you to write asynchronous code that looks sequential and is easy to read.

Setupโ€‹

Add to your build.gradle.kts:

dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}

Your First Coroutineโ€‹

import kotlinx.coroutines.*

fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello,")
}
// Output:
// Hello,
// World!

Key Conceptsโ€‹

suspend Functionsโ€‹

Functions that can be paused and resumed. Must be called from a coroutine or another suspend function:

import kotlinx.coroutines.*

suspend fun fetchData(): String {
delay(2000L) // Simulate network delay
return "Data loaded!"
}

fun main() = runBlocking {
println("Fetching...")
val result = fetchData()
println(result)
}

Coroutine Buildersโ€‹

BuilderDescription
runBlockingBlocks the thread, for testing/main functions
launchFire-and-forget, returns Job
asyncReturns Deferred<T> (future value)
coroutineScopeCreates a scope, suspends until children finish

launch โ€” Fire and Forgetโ€‹

import kotlinx.coroutines.*

fun main() = runBlocking {
val job = launch {
repeat(5) { i ->
println("Working... $i")
delay(500L)
}
}

println("Main continues")
job.join() // Wait for coroutine to finish
println("Done!")
}

async โ€” Get a Resultโ€‹

import kotlinx.coroutines.*

suspend fun getTemperature(): Int {
delay(1000L)
return 25
}

suspend fun getHumidity(): Int {
delay(800L)
return 60
}

fun main() = runBlocking {
val tempDeferred = async { getTemperature() }
val humidDeferred = async { getHumidity() }

// Both run concurrently!
val temp = tempDeferred.await()
val humid = humidDeferred.await()

println("Temp: $tempยฐC, Humidity: $humid%")
}

Coroutine Scopeโ€‹

import kotlinx.coroutines.*

fun main() = runBlocking {
coroutineScope {
launch {
delay(200L)
println("Task 1 done")
}
launch {
delay(100L)
println("Task 2 done")
}
}
println("All tasks done")
}
// Output:
// Task 2 done
// Task 1 done
// All tasks done

Coroutine Dispatchersโ€‹

Control which thread the coroutine runs on:

import kotlinx.coroutines.*

fun main() = runBlocking {
launch(Dispatchers.Main) { /* UI thread (Android) */ }
launch(Dispatchers.IO) { /* I/O operations */ }
launch(Dispatchers.Default) { /* CPU-intensive work */ }
launch(Dispatchers.Unconfined) { /* No specific thread */ }
}

Switching Dispatchersโ€‹

import kotlinx.coroutines.*

suspend fun loadData(): String = withContext(Dispatchers.IO) {
// Runs on IO thread
Thread.sleep(1000)
"Data from server"
}

fun main() = runBlocking {
val data = loadData()
println(data) // Prints on main thread
}

Job and Cancellationโ€‹

import kotlinx.coroutines.*

fun main() = runBlocking {
val job = launch {
try {
repeat(100) { i ->
println("Processing $i")
delay(100L)
}
} catch (e: CancellationException) {
println("Coroutine was cancelled")
} finally {
println("Cleanup done")
}
}

delay(500L)
job.cancel()
job.join()
println("Main ends")
}

Timeoutโ€‹

import kotlinx.coroutines.*

fun main() = runBlocking {
try {
withTimeout(1500L) {
repeat(10) { i ->
println("Processing $i")
delay(400L)
}
}
} catch (e: TimeoutCancellationException) {
println("Timed out!")
}
}

Flow โ€” Asynchronous Streamsโ€‹

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun numberFlow(): Flow<Int> = flow {
for (i in 1..5) {
delay(300L)
emit(i) // Emit value
}
}

fun main() = runBlocking {
numberFlow()
.map { it * it } // Transform
.filter { it > 5 } // Filter
.collect { println(it) } // Collect
}

Channel โ€” Communication Between Coroutinesโ€‹

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*

fun main() = runBlocking {
val channel = Channel<Int>()

launch {
for (i in 1..5) {
channel.send(i) // Producer
delay(100L)
}
channel.close()
}

for (value in channel) { // Consumer
println("Received: $value")
}
}

Summaryโ€‹

ConceptDescription
suspendMark a function as suspendable
runBlockingBridge between coroutines and blocking code
launchStart coroutine, get Job
asyncStart coroutine, get Deferred<T>
await()Get result from Deferred
delay()Suspend without blocking thread
withContext()Switch dispatcher within a coroutine
cancel()Cancel a running coroutine
FlowCold asynchronous stream
ChannelHot stream for producer-consumer pattern
Telemetry Integration

Completed working through this block? Sync progress to workspace.