What are trySend, tryReceive, and receiveCatching on a Channel, and when would you use them instead of send/receive?
answer
- trySend/tryReceive: non-suspending, return ChannelResult
- receiveCatching: suspends but no throw on close
- ChannelResult: isSuccess/isFailure/isClosed
- Use trySend from non-suspend callbacks
- offer/poll are deprecated
basics
~10 strySend and tryReceive are non-suspending attempts: they try once and tell you whether it worked, without waiting. receiveCatching waits but returns a result object instead of throwing when the channel is closed.
solid answer
~30 ssend/receive are suspending. trySend(element) and tryReceive() are their non-blocking, non-suspending counterparts: they attempt the operation immediately and return a ChannelResult<T> describing success, failure (channel full/empty), or closed — never suspending and never throwing for those conditions. They're useful in non-suspend contexts (callbacks, hot paths) or with CONFLATED/buffered channels where you want best-effort delivery. receiveCatching() does suspend (waits for a value) but, instead of throwing ClosedReceiveChannelException when the channel closes, returns a ChannelResult you inspect with getOrNull()/isClosed/exceptionOrNull(). This gives exception-free loop termination. The older offer/poll names are deprecated in favour of trySend/tryReceive returning ChannelResult.
go deeper
Knows send/receive suspend and that try-variants exist.
Uses trySend/receiveCatching and reads ChannelResult states correctly.
Chooses non-suspending variants to bridge callbacks and implement drop-based backpressure deliberately.
Designs callback/coroutine bridges (callbackFlow, custom adapters) with correct backpressure and closure semantics.
## Suspending vs non-suspending operations - `suspend fun send(e)` / `suspend fun receive()` — wait (suspend) until they can complete; throw on a closed channel. - `fun trySend(e): ChannelResult<Unit>` / `fun tryReceive(): ChannelResult<T>` — **do not suspend**. They attempt once and return immediately. - `suspend fun receiveCatching(): ChannelResult<T>` — **suspends** like `receive`, but reports closure via the result instead of throwing. ## ChannelResult A `ChannelResult<T>` is a small value type encoding one of three outcomes, inspected via: - `isSuccess` / `getOrNull()` — got/sent a value. - `isFailure` — couldn't proceed *right now* (full for send, empty for receive) — only meaningful for the `try*` calls. - `isClosed` / `exceptionOrNull()` — the channel is closed (with optional cause). ```kotlin import kotlinx.coroutines.channels.* val r = channel.trySend(value) when { r.isSuccess -> { /* delivered (or buffered) */ } r.isClosed -> { /* channel closed: r.exceptionOrNull() */ } r.isFailure -> { /* buffer full right now; dropped/retry */ } } ``` ## When to use the non-suspending forms - **Non-suspend contexts**: bridging a callback API or a UI event handler where you cannot call a `suspend` function. `trySend` from inside a `callbackFlow`'s producer is a classic case. - **Best-effort / backpressure-by-dropping**: with a CONFLATED or small buffered channel, `trySend` lets you drop instead of block. - **Hot paths** where suspension overhead is undesirable and missing is acceptable. ## When to use receiveCatching Use it to drain a channel **without** exception-based control flow: ```kotlin while (true) { val res = channel.receiveCatching() val v = res.getOrNull() ?: break // null => closed process(v) } ``` ## Deprecated predecessors `offer()` and `poll()` (which returned a bare value/Boolean) are deprecated; `trySend`/`tryReceive` with `ChannelResult` replaced them because the old API conflated "failed" with "closed."
- Does trySend ever suspend?No. It attempts once and returns a ChannelResult immediately — success, failure (full), or closed. It is callable from non-suspend code.
- Why prefer receiveCatching over wrapping receive in try/catch?It avoids exception-driven control flow for the expected closed case, which is clearer and cheaper than catching ClosedReceiveChannelException.
saying these in an interview costs you the question
- Saying trySend suspends or waits for space
- Confusing isFailure (full/empty now) with isClosed
- Using deprecated offer/poll in new code
- Thinking receiveCatching is non-suspending
- Ignoring the dropped value when trySend returns isFailure