skip to content

What do applyToEither and acceptEither do, and how do they differ from the 'both' family?

level: middleimportance: should knowfreq 52%

answer

  1. Either = race, first to settle wins
  2. applyToEither=Function, acceptEither=Consumer, runAfterEither=Runnable
  3. Both inputs share type T (one-arg callback)
  4. Loser is NOT cancelled, just ignored
  5. First to SETTLE — a first failure still propagates

basics

~10 s

The 'either' methods react to whichever of two futures finishes first. applyToEither passes that first result to a function; the 'both' methods instead wait for both futures to complete.

solid answer

~40 s

applyToEither, acceptEither and runAfterEither all complete as soon as the *first* of two futures finishes, ignoring the slower one. applyToEither takes a Function that receives the winning result and returns a new value; acceptEither takes a Consumer (side effect, no value); runAfterEither takes a Runnable (ignores the result). They are the 'race / whichever-first' counterpart to thenCombine/thenAcceptBoth/runAfterBoth, which wait for *both*. A natural use is hedging or fallback across redundant sources: query two replicas and take the faster reply. Both inputs must produce the same type for applyToEither (the function has one parameter). Note the inputs are not cancelled — the loser keeps running, it is just ignored. Exception handling is subtle: if the *first to settle* completes exceptionally, that exception propagates; a later failure of the loser is dropped.

go deeper

for a junior

Knows the 'either' methods react to whichever of two futures completes first and can name applyToEither.

for a middle

Explains the applyToEither/acceptEither/runAfterEither split, the shared result-type constraint, and the symmetry with the 'both' family.

for a senior

Articulates that it races on settlement (a first failure propagates), that the loser is not cancelled, and how to build a first-successful semantic instead.

for a principal

Designs hedging/timeout strategies weighing the cost of uncancelled losers, resource leaks, and pool pressure, and chooses between either-composition and anyOf-based aggregation.

## Setup: two parallel futures, but you only need one answer A `CompletableFuture<T>` is a placeholder for an asynchronous result. Sometimes you launch two computations that produce the **same kind** of answer and you only need **whichever comes back first** — a race. That is what the 'either' family is for. ## The three 'either' methods ``` CompletableFuture<V> applyToEither(CompletionStage<? extends T> other, Function<? super T, V> fn) CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action) ``` Read `applyToEither` as: 'I produce a `T`; `other` also produces a `T`; when the **first of us** completes, take that result, run `fn` on it, and that becomes the new future's value.' Because the callback receives **one** result (only the winner's), both inputs must share the result type `T` — that is why it is a `Function` (one arg), not a `BiFunction`. - `applyToEither` → a `Function`, returns a new value. - `acceptEither` → a `Consumer` (takes the winning value, returns nothing) → `CompletableFuture<Void>`. - `runAfterEither` → a `Runnable` (ignores the value entirely) → `CompletableFuture<Void>`. ## 'Either' vs 'Both' — the symmetry Every 'both' method has an 'either' twin. They are mirror images of the same composition pattern: | Wait for BOTH | Wait for EITHER (first) | Callback shape | |---|---|---| | `thenCombine` | `applyToEither` | function returning a value | | `thenAcceptBoth` | `acceptEither` | consumer (side effect) | | `runAfterBoth` | `runAfterEither` | runnable (ignore results) | 'Both' = a **join** (need all data). 'Either' = a **race** (need any answer, fastest wins). ## What the loser does A crucial subtlety: the **slower** future is **not cancelled**. It keeps running to completion; its result (or exception) is simply discarded. If that work holds resources (a DB connection, a thread), you pay for it anyway. For true cancellation you must wire it up yourself. ## Failure semantics If the **first future to settle** settles **exceptionally**, that exception is what propagates to the result — even if the other future would have succeeded a moment later. So 'either' races on *completion*, not on *success*. If you want 'first **successful** result, ignore failures', the 'either' methods are **not** enough; you need extra logic (e.g. combine with `exceptionally` per branch, or `anyOf` over already-guarded futures). ## Typical use cases - **Hedged requests:** send the same query to two replicas; take the faster. - **Timeout fallback:** race the real call against a future that completes with a default after a delay. ## Deriving your answer - Junior: 'reacts to whichever future finishes first.' - Middle: add the same-type constraint, the both-vs-either symmetry, and that the loser is not cancelled. - Senior: add the 'settles first, not succeeds first' failure subtlety and why anyOf+per-branch guards differ.

  • If the faster of the two futures fails with an exception, does applyToEither fall back to the slower one?
    No. applyToEither races on completion, not on success. If the first to settle settles exceptionally, that exception propagates and the function is not applied. To prefer the first SUCCESS you need per-branch exceptionally guards or different composition.
  • Does using acceptEither cancel the losing future?
    No. The loser is not cancelled; it runs to completion and its result is discarded, so any resources it holds are still consumed.

saying these in an interview costs you the question

  • Believing 'either' gives the first SUCCESSFUL result — it gives the first to settle, success or failure
  • Assuming the slower future is cancelled — it keeps running
  • Trying to use applyToEither when the two futures produce different types
  • Confusing acceptEither (Consumer, side effect) with applyToEither (Function, value)

context