skip to content

What do orTimeout and completeOnTimeout do, and how do they differ from each other and from get(timeout)?

level: seniorimportance: should knowfreq 50%

answer

  1. Java 9+; deadline on the FUTURE, not the caller
  2. orTimeout → completeExceptionally(TimeoutException)
  3. completeOnTimeout → complete(fallbackValue)
  4. get(timeout) bounds only the caller's wait, future keeps running
  5. underlying task is NOT cancelled, just abandoned
  6. return the same future, chainable

basics

~20 s

Both (Java 9+) put a time limit on the future itself, not just on a waiting caller. orTimeout fails it with a TimeoutException if it's late; completeOnTimeout finishes it with a fallback value instead. Unlike get(timeout), they actually settle the future.

solid answer

~50 s

`orTimeout(timeout, unit)` and `completeOnTimeout(value, timeout, unit)` (Java 9+) attach a deadline to the **future itself**. If the future hasn't completed by then, `orTimeout` completes it **exceptionally** with a `TimeoutException`, while `completeOnTimeout` completes it **successfully** with the supplied fallback value. Both return the same `CompletableFuture` (for chaining) and use an internal scheduled thread — you don't manage a timer. The contrast with `get(timeout, unit)` is fundamental: `get(timeout)` only bounds the **caller's wait** and throws `TimeoutException` to that one caller, leaving the future still running for everyone else; `orTimeout`/`completeOnTimeout` mutate the **shared future's state**, so every downstream stage and every other consumer sees the timeout outcome. Caveat: the underlying async work is **not cancelled** — it keeps running, you've just stopped waiting on its result. For true cancellation you must also cancel the task or interrupt its thread.

go deeper

for a junior

Knows orTimeout makes a future fail after a delay and completeOnTimeout gives it a default value after a delay.

for a middle

Uses both correctly (Java 9+), knows orTimeout yields a TimeoutException downstream and completeOnTimeout a fallback value, and chains them.

for a senior

Contrasts them sharply with get(timeout) (future-state vs caller-wait), knows the task isn't cancelled, and handles the wrapped TimeoutException in exceptionally/handle.

for a principal

Reasons about resource leaks from abandoned-but-running tasks, pairs timeouts with real cancellation/interruption, and considers the shared scheduler's load and timeout-vs-result race semantics across a pipeline.

### The gap these fill Before Java 9, putting a deadline on a `CompletableFuture` was awkward — you had to schedule your own timer task that called `completeExceptionally`/`complete`. Java 9 added two built-in methods that bake the deadline **into the future**. ### orTimeout(timeout, unit) ```java CompletableFuture<T> orTimeout(long timeout, TimeUnit unit); ``` If the future is **not** complete when the timeout elapses, it is completed **exceptionally** with a `java.util.concurrent.TimeoutException`. If it completes normally first, the timeout is harmless (the future was already settled, so the scheduled completion is a no-op). Returns the **same** future so you can chain. ```java cf.orTimeout(2, TimeUnit.SECONDS) .exceptionally(ex -> { // ex is a CompletionException wrapping TimeoutException log.warn("timed out", ex); return fallback; }); ``` ### completeOnTimeout(value, timeout, unit) ```java CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit); ``` Same deadline mechanism, but on expiry it completes the future **successfully** with `value` — a graceful default instead of an error. Useful when a stale/placeholder answer is better than failing. ```java prices.completeOnTimeout(CACHED_PRICE, 200, TimeUnit.MILLISECONDS); ``` ### How they differ from get(timeout, unit) This is the heart of the question: | | `get(timeout)` / waiting | `orTimeout` / `completeOnTimeout` | |---|---|---| | What it bounds | the **caller's wait** | the **future's lifetime** | | Effect on the future | none — keeps running | **completes it** (exceptionally / with value) | | Who observes the timeout | only the one blocked caller | **all** downstream stages & consumers | | Returns / throws | throws `TimeoutException` to that caller | returns the same future for chaining | So `get(timeout)` is a *local* patience limit; `orTimeout`/`completeOnTimeout` are a *global* completion policy on the shared object. ### The crucial caveat: the work is not stopped Neither method **cancels the underlying computation**. If a `supplyAsync` task is grinding away, `orTimeout` firing just sets the future's result to a `TimeoutException` — the task thread keeps running to completion (and its eventual result is discarded because the future is already settled, by the first-wins rule). If you need to actually stop the work, you must separately cancel the task / interrupt its thread; these timeout helpers only settle the **result holder**. ### Scheduling cost Timeouts are driven by an internal single-thread scheduled executor (a JDK-internal "Delayer"). It is shared and lightweight, but be aware that every `orTimeout` schedules a delayed task; on a successful early completion the future is settled first and the scheduled firing becomes a no-op. ### Combining with completion control These compose with the manual completers: an `orTimeout` is essentially a built-in, scheduled `completeExceptionally(new TimeoutException())`, and `completeOnTimeout` a scheduled `complete(value)` — both subject to the same first-wins race, so whichever of {real result, timeout} fires first wins.

  • Does orTimeout interrupt or cancel the task that's still computing?
    No. It only completes the future exceptionally with a TimeoutException. The underlying computation keeps running; its eventual result is discarded because the future is already settled. Cancelling the task requires separate action.
  • How would you implement orTimeout's behavior manually pre-Java-9?
    Schedule a delayed task on a ScheduledExecutorService that calls completeExceptionally(new TimeoutException()) on the future; first-wins ensures it's a no-op if the real result arrives first.

saying these in an interview costs you the question

  • Thinking orTimeout cancels/stops the running task — it only settles the result
  • Confusing get(timeout) (caller-local) with orTimeout (future-global)
  • Assuming completeOnTimeout throws — it completes successfully with the fallback
  • Believing these existed before Java 9

context