Skip to content

Chapter 7 of 7

Coroutines and Flow

Coroutines are Kotlin's framework for writing asynchronous code that looks synchronous. A coroutine is a lightweight, cooperatively scheduled unit of work that can be paused and resumed at suspension points without blocking the underlying thread — so thousands of coroutines can share a small thread pool efficiently. Functions that participate in this model are declared with the suspend keyword and can only be called from another suspend function or from a coroutine builder. dispatchers determine which thread or thread pool a coroutine runs on: Dispatchers.Main for UI work, Dispatchers.IO for blocking I/O, Dispatchers.Default for CPU-bound computation, and Dispatchers.Unconfined when the dispatcher should be inherited from the calling context. The withName property on the standard dispatchers adds a label that surfaces in coroutine stack traces and thread dumps, making debugging easier.

Two builder functions cover most needs. launch starts a coroutine that returns a Job — useful for fire-and-forget work without a result. async starts a coroutine that returns a Deferred<T>, on which you call await() to retrieve the value, and is the natural choice when you need a return value. withContext(dispatcher) { ... } switches the dispatcher for the duration of the block and returns to the original context on completion — the idiomatic way to move work off the main thread. A Job is the handle to a launched coroutine, exposing cancel(), join(), isActive, and isCompleted, while a Deferred<T> extends Job with the await() method.

Structured concurrency means coroutines are scoped to a parent and cannot outlive it. The coroutineScope { ... } builder creates a child scope in which all launched children must complete before the block returns; if any child fails, its siblings are cancelled and the failure is propagated. supervisorScope { ... } differs in that child failures are isolated — siblings continue, and the parent only fails if you explicitly await the failed child. A SupervisorJob as the parent (used by default in viewModelScope and GlobalScope in many setups) prevents sibling cancellation on failure, ideal for fire-and-forget tasks where one failure should not bring down the rest. viewModelScope ties coroutines to the ViewModel's lifecycle, automatically cancelling them on onCleared() to prevent leaks.

Cancellation in coroutines is cooperative. Job.cancel() sends a signal that propagates to the next suspension point, where the coroutine throws a CancellationException that the coroutine machinery treats as normal completion. To honor cancellation properly, suspending functions should call ensureActive() or other cancellable APIs. withContext(NonCancellable) { ... } runs work that ignores cancellation signals, useful for cleanup that must finish before the coroutine truly ends — but should be used sparingly. Finally, Flow is Kotlin's cold streaming counterpart to suspend functions. A suspend fun returns a single value, whereas a Flow emits many values over time, supports operators like map, filter, collect, combine, and debounce, and runs lazily — emitting only when a collector subscribes. Cold flows built with flow { ... } run a fresh producer for each subscriber, giving every collector its own stream. Hot flows like StateFlow, SharedFlow, and Channel emit whether or not anyone is collecting, sharing values among subscribers and behaving like event buses. Together, suspend functions, builders, and Flow form a complete model for handling asynchronous work — from single network calls to reactive UI pipelines — while keeping the calling code readable.

All chapters
  1. 1Foundations — Types, Variables, and Expressions
  2. 2Null Safety
  3. 3Classes, Objects, and Inheritance
  4. 4Functions, Lambdas, and Scope Functions
  5. 5Properties and Delegation
  6. 6Collections, Sequences, and Strings
  7. 7Coroutines and Flow

Drill it

Reading is not remembering. These come from the Kotlin Programming deck:

Q

What is null safety in Kotlin?

Null safety is a Kotlin feature that eliminates NullPointerException at compile time.By default, variables cannot hold null. You must explicitly declare a type...

Q

How do you declare a nullable type in Kotlin?

Append a ? to the type declaration:var name: String? = nullWithout the ?, the compiler will reject any attempt to assign null.

Q

What is the safe call operator in Kotlin?

The safe call operator ?. allows you to access a property or call a method on a nullable object without risking a NullPointerException:val length = name?.length...

Q

What is the Elvis operator in Kotlin?

The Elvis operator ?: provides a default value when an expression is null:val len = name?.length ?: 0If the left side is null, the right side is used.