skip to content

When you fan out into a list of Deferreds, what does awaitAll do, and how does it behave on failure compared to awaiting in a loop?

level: middleimportance: should knowfreq 55%

answer

  1. awaitAll = wait all, ordered List<T>
  2. fails fast on first failure
  3. loop awaits in order, slower to surface
  4. results in input order, not completion
  5. map { async }.awaitAll() pattern

basics

~10 s

awaitAll waits for every Deferred in a list and returns all their results as a list. If any one fails, awaitAll fails fast immediately instead of waiting for the slower remaining ones.

solid answer

~40 s

awaitAll is an extension (on Collection<Deferred<T>> and as a vararg) that suspends until all the given Deferreds complete and returns List<T> in input order. Its key advantage over a manual `deferreds.map { it.await() }` loop is failure timing: awaitAll **fails fast** — the moment any Deferred fails, awaitAll throws that exception right away rather than awaiting earlier-in-the-list slow ones first. Under structured concurrency inside coroutineScope, a child failure also cancels its siblings, so a failure in one fan-out task tears down the rest and propagates. Use it for parallel decomposition over a collection: `val results = items.map { async { process(it) } }.awaitAll()`. Order of results matches the list, regardless of which finished first.

code

kotlin · 3 lines
kotlin
suspend fun fetchPages(urls: List<String>): List<String> = coroutineScope {
    urls.map { url -> async { httpGet(url) } }.awaitAll()
}

go deeper

for a junior

Knows awaitAll returns a list of all results from a list of Deferreds.

for a middle

Explains fail-fast vs loop ordering and input-order results; uses the map+async+awaitAll pattern.

for a senior

Connects fail-fast to structured cancellation of siblings and chooses supervisorScope/per-task catching for partial results.

for a principal

Designs fan-out with concurrency caps, error-aggregation strategy, and clear all-or-nothing vs best-effort contracts.

## What `awaitAll` does `awaitAll` waits for a **collection of `Deferred`** to all complete and returns their results as a `List<T>` in the **same order as the input** (not completion order). Two overloads exist: - `Collection<Deferred<T>>.awaitAll(): List<T>` - `awaitAll(vararg deferreds: Deferred<T>): List<T>` ```kotlin suspend fun processAll(items: List<Item>): List<Result> = coroutineScope { items .map { item -> async { process(item) } } // fan out, all start eagerly .awaitAll() // fan in, ordered results } ``` ## awaitAll vs. a manual loop A hand-written `deferreds.map { it.await() }` awaits in **list order**. If the third item fails fast but the first is slow, the loop is still stuck on the first `await()` and only surfaces the failure later. **`awaitAll` fails fast**: it throws as soon as *any* Deferred completes exceptionally, no matter its position in the list. ## Failure + structured concurrency Inside `coroutineScope { }`, the `async` children are structured. If one child throws: 1. The failing child completes exceptionally. 2. The parent `coroutineScope` is cancelled, which **cancels the sibling Deferreds**. 3. `awaitAll` (and the enclosing `coroutineScope`) rethrows the original exception. So one bad task tears down the whole fan-out — usually what you want for all-or-nothing work. If you need partial results, wrap each task to catch its own exception (e.g., return a sealed result) or use `supervisorScope`. ## Ordering guarantee Results come back in **input order**, decoupled from which coroutine finished first. This makes zipping results back to inputs trivial. ## Key APIs/keywords - `awaitAll()` — fail-fast, ordered fan-in - `List<Deferred<T>>` produced by `map { async { } }` - `coroutineScope` / `supervisorScope` to control failure propagation

  • Are results returned in completion order or input order?
    Input order. awaitAll preserves the order of the original collection regardless of which Deferred finished first.
  • How would you get partial results instead of all-or-nothing?
    Catch exceptions inside each async (return a Result/sealed type), or run under supervisorScope so one failure doesn't cancel siblings.

saying these in an interview costs you the question

  • Saying results come back in completion order
  • Claiming awaitAll waits for all even after one fails (it fails fast)
  • Not realizing a child failure cancels siblings under coroutineScope
  • Using awaitAll when partial results are needed without supervisorScope or per-task catching

context