Exception-combinators
advanced16 min readLesson 166 of 180
exceptionally vs handle vs whenComplete, recovery placement, and orTimeout/completeOnTimeout.
Exception-combinators differ in what they pass and what they return:
exceptionally(fn)— runs only on failure; fn receives the throwable, returns a replacement RESULT. Skipped on success.handle(fn)— runs ALWAYS; fn receives(result, throwable)— exactly one is null. The universal transform.whenComplete(fn)— runs always; fn observes(result, throwable)but CANNOT replace the outcome (returns the same future). Observability.
future.handle((ok, err) -> err == null ? ok : fallback);
Chaining recovery on a dependent stage: future.thenApply(...).exceptionally(...)
recovers failures from BOTH stages — attach recovery at the level you want to
recover from. Attaching exceptionally before thenApply leaves the map
step exposed.
Time bounds: orTimeout(500, MILLISECONDS) completes the future
exceptionally (TimeoutException) if it takes longer; completeOnTimeout(fallback, 500, MILLISECONDS)
substitutes a value instead. Both schedule internally — no sleeping threads.