Kotlin's select Clause Solves the Partial-Results Problem That awaitAll Can't
Many real-world UIs need to show results as they arrive rather than waiting for the slowest backend. `select` is the idiomatic Kotlin coroutine primitive for that pattern, but it's underused partly because the API's stability markers are inconsistent and partly because developers reach for `Channel` or `launch` workarounds that add more concurrency machinery than necessary.
A common e-commerce shipping-quote requirement exposes the limits of `async` + `awaitAll`: three carriers are queried in parallel, but the UI must show whatever returns within 3 seconds, not wait for the slowest one. Wrapping `awaitAll` in `withTimeoutOrNull` discards already-completed results because `awaitAll` only delivers a complete list.
`select` with `onAwait` solves this by returning as soon as any single `Deferred` finishes. A loop collects results until a timeout, and if nothing arrives, a second phase waits for the first non-null quote using the same still-running `Deferred` instances. The approach keeps the code single-threaded within the calling coroutine, so a plain `MutableMap` is enough.
The same `select` expression also handles `Channel` sends and receives, and `Job` completion, making it a general-purpose multiplexer for coroutine events. Two production pitfalls stand out: a failed `Deferred` throws instead of returning null, so error-conversion rules must be explicit, and fallback waiting needs its own deadline so it doesn't hang forever.
`select` is a stable API but sits alongside experimental and internal annotations, which creates confusion about whether it's safe for production use. The official docs still label it experimental despite the core functions being callable without opt-in flags.
Many Kotlin developers treat `select` as an obscure corner of the language, yet the pattern it solves — racing multiple async operations and processing partial results — is common in mobile and frontend work where UIs can't block on the slowest backend.
The `select`-plus-loop pattern keeps result collection single-threaded within the calling coroutine, avoiding the need for concurrent data structures that `launch`-based solutions would require.
The two-phase approach (collect within a window, then fall back to first-available) mirrors real product requirements more closely than a single timeout or a single `awaitAll`, and `select` makes it straightforward without restarting requests.