Coroutines
02 / 02

Dispatchers, Cancellation & Flow

Dispatchers, Cancellation & Flow

Dispatchers & withContext

suspend fun loadUser(id: Long): User = withContext(Dispatchers.IO) {
    database.getUser(id)  // temporarily switch to IO dispatcher, then return
}

A Dispatcher determines which thread pool a coroutine's code runs on — Dispatchers.IO for blocking I/O, Default for CPU-intensive work, Main for UI-thread work on Android. withContext temporarily switches dispatchers for a block, then returns to the original one — the standard idiom for moving a specific operation off the main thread.

Cooperative Cancellation

Cancellation is cooperative, not preemptive: a coroutine's code must check for and respond to cancellation (checked automatically at suspension points, or explicitly via isActive/ensureActive()). A tight, non-suspending loop won't actually stop just because cancellation was requested — it needs an explicit check. This mirrors cooperative vs. preemptive scheduling generally: control transfers only at defined suspension points, not at arbitrary instruction boundaries.

Concurrent Awaiting & Flow

val a = async { fetchA() }
val b = async { fetchB() }
val result = a.await() + b.await()  // both run concurrently, not sequentially

Launching independent operations concurrently (rather than awaiting each sequentially) reduces total wall-clock time. Flow extends the coroutine model to multi-value async streams over time — the coroutine analogue of an Observable — complementing a plain suspend function's single-value return.

The Tradeoff

Coroutines add real conceptual overhead (suspend functions, structured concurrency, dispatchers) that pays off clearly for complex or highly concurrent async workflows, but might be more machinery than a trivially simple, one-off asynchronous operation strictly needs.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free