Working with CompletableFuture

1. Creating CompletableFuture

FactoryUse
completedFuture(v)Already complete
failedFuture(t)Already failed (Java 9+)
supplyAsync(supplier)Async with result
runAsync(runnable)Async no result
Custom executor2nd arg of supplyAsync/runAsync

2. Running Tasks Asynchronously

Example: Async

CompletableFuture<String> cf = CompletableFuture
    .supplyAsync(() -> fetchData(), executor);
Default ExecutorDetail
Common ForkJoinPoolShared with parallel streams
CautionLong-running tasks can starve pool

3. Chaining with thenApply()

MethodMaps
thenApply(fn)T → U (sync)
thenApplyAsync(fn)T → U (async)

4. Chaining with thenAccept()

MethodDetail
thenAccept(consumer)Side effect, returns CF<Void>
thenRun(runnable)No input, no output

5. Chaining with thenCompose()

Example: thenCompose vs thenApply

CompletableFuture<User> user = fetchUser(id);
CompletableFuture<Order> order = user.thenCompose(u -> fetchOrder(u.id()));    // flatMap-like
CompletableFuture<CompletableFuture<Order>> nested = user.thenApply(u -> fetchOrder(u.id()));  // wrong!
MethodUse
thenCompose(fn)fn returns CompletableFuture (flatMap)

6. Combining with thenCombine()

MethodUse
thenCombine(other, biFn)Wait for BOTH, combine results
thenAcceptBothConsume both
runAfterBothNo values

7. Handling Multiple Futures (allOf, anyOf)

Example: allOf

List<CompletableFuture<String>> futures = urls.stream()
    .map(this::fetchAsync).toList();

CompletableFuture<Void> all = CompletableFuture
    .allOf(futures.toArray(CompletableFuture[]::new));

List<String> results = all.thenApply(v ->
    futures.stream().map(CompletableFuture::join).toList()
).join();
MethodReturns
allOf(...)CF<Void> — completes when all done
anyOf(...)CF<Object> — first to complete

8. Handling Exceptions

MethodDetail
exceptionally(fn)Recover from exception
handle((v, e) -> ...)Process success and failure
whenComplete((v, e) -> ...)Side effect, doesn't transform
exceptionallyCompose(fn) JAVA 12+Recover with another CF

9. Using Custom Executors

AspectDetail
Per-callPass to ...Async variants
WhyIsolate workloads, set bounded threads

10. Setting Timeouts JAVA 9+

Example: Timeout

CompletableFuture<String> cf = fetchAsync()
    .orTimeout(5, TimeUnit.SECONDS)
    .completeOnTimeout("default", 4, TimeUnit.SECONDS);
MethodDetail
orTimeout(t,u)Fail with TimeoutException
completeOnTimeout(v,t,u)Use default value

11. Cancelling Futures

MethodDetail
cancel(mayInterrupt)Mark cancelled — does NOT interrupt actual computation
complete(v) / completeExceptionally(t)Manual completion
obtrudeValue/obtrudeExceptionForce overwrite (use carefully)

12. Getting Results

MethodDetail
get()Throws checked InterruptedException/ExecutionException
get(t, u)Timed get
join()Unchecked variant (CompletionException)
getNow(default)Non-blocking, default if not done

13. Composing Async Pipelines

PatternDetail
SequentialthenCompose chain
ParallelallOf + join
RaceanyOf or applyToEither
Branch + mergethenCombine after parallel