Java · OOP, JVM and backend craft

How does CompletableFuture support asynchronous composition?

HardcompletablefutureasynccompositionUpdated Oct 2026
Quiz this track

Short answer

CompletableFuture composes asynchronous work through callbacks and combinators. It supports chaining, combining, fallbacks and fan-out without blocking a thread per step.

A CompletableFuture represents a result that will exist later. You attach dependent stages with thenApply, thenCompose and thenCombine, and handle failure with exceptionally or handle. The default async methods run on the common ForkJoinPool unless you pass an executor.

java
CompletableFuture<String> result =
    CompletableFuture.supplyAsync(() -> fetchUser(userId), executor)
        .thenCompose(user -> fetchOrders(user))
        .thenApply(orders -> summarize(orders))
        .exceptionally(error -> "fallback");

String value = result.join();
thenCompose flattens a stage that itself returns a future. exceptionally provides a fallback value.

The combinators that matter

  • thenApply transforms a result synchronously; thenCompose chains another async stage
  • thenCombine merges two independent futures when both complete
  • allOf waits for all futures; anyOf completes with the first success
  • exceptionally and handle recover; whenComplete observes without changing the value
  • orTimeout and completeOnTimeout bound how long a stage may take

Use a custom executor for blocking IO. The common pool is sized for CPU work, and blocking its threads can starve unrelated tasks. Also avoid calling join on a request thread without a timeout, because that reintroduces the blocking you were trying to remove.

Exceptions inside a stage are wrapped in CompletionException. Use handle or exceptionally to unwrap and translate them into domain errors before they reach the caller.

Follow-ups you should expect

Interviewers use these to check whether the first answer was understanding or memorisation.

  1. 1What is the difference between thenApply and thenCompose?
  2. 2How do you combine two independent futures?
  3. 3Why use a custom executor with CompletableFuture?
  4. 4How do you add a timeout to an async stage?

Say it in the interview

CompletableFuture represents an asynchronous result and lets me compose stages without blocking. thenApply transforms a value, thenCompose chains another async call, and thenCombine merges independent results. I handle failures with exceptionally or handle and add timeouts for external calls. For blocking IO I pass a dedicated executor, because the common pool is sized for CPU work and should not be starved by blocking tasks.

Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.

Related questions

Reading is step one

Now say it out loud.

Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Java mock interview and get scored feedback on depth, clarity and confidence.