How do you inspect a CompletableFuture's state without blocking, and what do getNow, isDone, isCancelled, and isCompletedExceptionally tell you?
answer
- isDone = settled any way (success/fail/cancel)
- isCancelled = cancelled only
- isCompletedExceptionally = failed OR cancelled
- success = isDone && !isCompletedExceptionally
- getNow(default): value, or default if pending, throws if failed
- snapshot only — check-then-act is racy
basics
~20 sThese methods peek at the future without waiting. isDone tells you if it's finished (any way). isCancelled and isCompletedExceptionally tell you how it finished. getNow(default) returns the value right now if ready, otherwise your default — it never blocks.
solid answer
~40 sFor non-blocking inspection: `isDone()` returns true if the future is settled in any way — success, exception, **or** cancellation. To distinguish how, `isCancelled()` is true only for cancelled futures, and `isCompletedExceptionally()` is true for **both** exceptional completion **and** cancellation (cancellation is a special exceptional completion via CancellationException). So a successful future is `isDone() && !isCompletedExceptionally()`. `getNow(valueIfAbsent)` is the non-blocking value reader: if the future is already completed successfully it returns the value; if it failed it throws (CompletionException/CancellationException); if it's still incomplete it returns your fallback immediately. These are mostly for monitoring, logging, fast-path optimizations, or speculative reads — not for control flow, because the state can change the instant after you check it (a check-then-act race). For real reactions to completion you should compose with whenComplete/handle rather than poll these flags.
go deeper
Knows isDone tells whether the future finished and getNow returns a default if it's not ready yet.
Distinguishes isCancelled vs isCompletedExceptionally and derives success = isDone && !isCompletedExceptionally; knows getNow throws on failure.
Explains cancellation-as-exceptional-completion overlap, getNow's three outcomes, and why these are snapshots unsuitable for control flow (check-then-act races).
Reserves inspection for observability/metrics and fast-path reads, mandates whenComplete/handle for actual reaction logic, and reasons about visibility/ordering between isDone and dependent-stage execution.
### Why inspect without blocking? `get()`/`join()` **block** until the future settles. Sometimes you just want to **peek** — for a dashboard, a log line, a fast path, or a speculative read — without parking the thread. CompletableFuture offers several non-blocking inspectors. ### isDone() `boolean isDone()` — true once the future is **settled in any way**: completed successfully, completed exceptionally, **or** cancelled. It does **not** tell you the outcome, only that the waiting is over. A subtle point: `isDone()` becomes true the moment the result is set, but **dependent stages may not have run yet** — it reflects the future's own state, not the whole chain. ### isCancelled() `boolean isCancelled()` — true **only** if the future was cancelled (someone called `cancel(...)` and won the completion race). False for normal success and for ordinary exceptional completion. ### isCompletedExceptionally() `boolean isCompletedExceptionally()` — true if the future ended in a failure **including cancellation**. This is the gotcha: **cancellation is modeled as an exceptional completion** (with a `CancellationException`), so a cancelled future reports `isCompletedExceptionally() == true` **and** `isCancelled() == true`. ### Deriving the full picture From these three you can classify any settled future: - **Success:** `isDone() && !isCompletedExceptionally()` - **Cancelled:** `isCancelled()` (also exceptional) - **Failed (non-cancel):** `isCompletedExceptionally() && !isCancelled()` - **Still running:** `!isDone()` ### getNow(valueIfAbsent) ```java T getNow(T valueIfAbsent); ``` The non-blocking value reader: - Already completed **successfully** → returns the value. - Already completed **exceptionally** (incl. cancelled) → **throws** (`CompletionException` / `CancellationException`) — `getNow` is not exception-safe by itself. - **Not yet completed** → returns `valueIfAbsent` immediately, no blocking. Great for a fast path: "if the answer is already cached/ready, use it; otherwise use a placeholder and move on." ### The big caveat: check-then-act is racy All of these report a **snapshot**. Between `if (cf.isDone())` and the next line, another thread can complete or cancel the future. So: ```java if (cf.isDone()) { String v = cf.getNow(null); // could STILL throw if it became exceptional, or be a different state } ``` is not a safe control-flow idiom. Use these for **observability and optimization**, not for coordinating logic. For reacting to completion, compose: ```java cf.whenComplete((value, ex) -> { /* runs exactly once, race-free */ }); ``` ### Quick example ```java CompletableFuture<String> cf = new CompletableFuture<>(); cf.isDone(); // false cf.getNow("pending"); // "pending" (not completed yet) cf.complete("ready"); cf.isDone(); // true cf.isCompletedExceptionally(); // false cf.getNow("pending"); // "ready" ```
- Is isCompletedExceptionally() true for a cancelled future?Yes. Cancellation is modeled as exceptional completion with a CancellationException, so both isCancelled() and isCompletedExceptionally() are true.
- What does getNow return on a still-incomplete future?It returns the valueIfAbsent argument immediately without blocking. It only returns the real value if already completed successfully, and throws if completed exceptionally.
saying these in an interview costs you the question
- Assuming isCompletedExceptionally is false for a cancelled future — it's true
- Thinking getNow blocks or returns null on failure — it throws on failure
- Using isDone() for control flow as if it were race-free
- Believing isDone() means all dependent stages have also finished