What is the `select` expression in Kotlin coroutines, and what problem does it solve?
answer
- Wait on many suspending sources, take the first ready
- Clauses: onReceive, onSend, onAwait, onTimeout
- Biased: top-to-bottom, first listed ready wins
- select<R> { } — all clauses return R
- No extra coroutine, no busy-wait
basics
~10 sselect lets a coroutine wait on several suspending operations at once and proceed with whichever finishes first, ignoring the rest. It is like racing multiple options and taking the winner.
solid answer
~40 s`select { }` is a suspending expression that waits simultaneously on multiple clauses and resumes with the result of the first one that becomes ready, cancelling its participation in the others. Each clause is a special function: `onReceive` (channel has an element), `onSend` (channel can accept), `onAwait` (a Deferred completed), `onTimeout` (a duration elapsed). It is biased: clauses are checked top-to-bottom, so an earlier ready clause wins. `select` returns whatever value its winning lambda produces, so all clause lambdas must return the same type R as `select<R> { }`. It solves the problem of racing or multiplexing several asynchronous sources without spawning extra coroutines or busy-waiting.
code
kotlin · 8 linesimport kotlinx.coroutines.*
import kotlinx.coroutines.selects.select
suspend fun race(fast: Deferred<String>, slow: Deferred<String>): String =
select {
fast.onAwait { it }
slow.onAwait { it }
}go deeper
Can explain that select waits on several sources and takes the first ready, and name a couple of clauses.
Knows the bias/ordering, the shared return type R, and that only the winning clause's effect happens.
Can contrast select with launching racing coroutines, and reason about starvation from bias and resource cleanup of losers.
Frames select within structured concurrency and back-pressure design, weighing its experimental status and fairness trade-offs at scale.
## What `select` is `select` is a coroutine builder-like expression from `kotlinx.coroutines` that lets a single coroutine **wait on multiple suspending operations at the same time** and continue with **the first one that is ready**. Once a clause wins, the others are not performed. Think of it as a `when`/`switch` whose branches are not boolean conditions but **suspending events**: "the moment a channel has data", "the moment a `Deferred` completes", "the moment a timeout fires". ## Why it exists Without `select`, awaiting whichever of two things happens first forces you to launch extra coroutines and coordinate them manually. `select` does this in one coroutine, with no busy-waiting (no polling loop burning CPU). It **suspends** until exactly one source becomes ready. ## The clause functions Clauses are member functions exposed via `SelectBuilder`. Common ones: - `channel.onReceive { value -> ... }` — fires when the channel has an element to receive. - `channel.onSend(value) { ... }` — fires when the channel can accept a send. - `deferred.onAwait { result -> ... }` — fires when an `async`/`Deferred` completes. - `onTimeout(duration) { ... }` — fires after the given time elapses (an escape hatch / race against a deadline). Each clause takes a lambda whose body runs only if that clause wins. ## Return value and typing `select<R> { }` returns a value of type `R`. **Every** clause lambda must return that same type. You almost always write `select` so the compiler can infer `R` from the lambda bodies. ```kotlin import kotlinx.coroutines.* import kotlinx.coroutines.channels.* import kotlinx.coroutines.selects.select suspend fun firstReady(a: ReceiveChannel<Int>, b: ReceiveChannel<Int>): String = select { a.onReceive { v -> "from a: $v" } b.onReceive { v -> "from b: $v" } } ``` ## Key behaviors - **Biased/ordered:** clauses are tried top-to-bottom; if several are simultaneously ready, the first listed wins. This makes `select` deterministic for ties (and can starve later clauses if you are not careful). - **Suspending:** if no clause is ready, `select` suspends until one becomes ready. - **Single winner:** only the winning clause's effect happens; for example, an `onReceive` that loses does **not** consume an element. Use `select` for racing requests, multiplexing channels, or adding timeouts inline.
- If two clauses are ready at the exact same time, which one wins?The one listed first in the `select` block — `select` is biased and evaluates clauses top-to-bottom.
- Does a losing `onReceive` clause consume an element from its channel?No. Only the winning clause performs its operation; the other channels are left untouched.
Like watching several lottery draws at once and acting on whichever number comes up first, then walking away from the rest.
saying these in an interview costs you the question
- Thinking `select` runs all clauses and combines their results
- Saying it busy-polls or burns CPU while waiting
- Believing the winner is random rather than biased to the first listed
- Claiming each clause must be launched in its own coroutine
- Forgetting that all clause lambdas must return the same type