Careeroza
CompletableFuture
Async Pipelines
Future.get() blocks. CompletableFuture (Java 8) lets you describe what happens when the result arrives: transform it, chain another async call, combine two results, or recover from errors — without blocking threads.
| Method | Like | Use |
|---|---|---|
supplyAsync(supplier, executor) | start async work that returns a value | |
thenApply(fn) | map | transform the result |
thenCompose(fn) | flatMap | chain another async call that returns a CompletableFuture |
thenCombine(other, fn) | zip | combine two independent results |
thenAccept(consumer) | forEach | consume the result |
exceptionally(fn), handle(fn) | catch | recover from errors |
allOf(...), anyOf(...) | wait for all / the first | |
orTimeout, completeOnTimeout (Java 9) | timeouts | |
join() | get() | block for the result (unchecked exception) |
Product Page Aggregation
Example
A product page needs details, price and reviews from three services. Details and price run in parallel, then reviews are chained.
import java.util.concurrent.*;
public class Main {
static String sleepAndReturn(int ms, String value) {
try { Thread.sleep(ms); } catch (InterruptedException e) { throw new RuntimeException(e); }
return value;
}
public static void main(String[] args) {
try (ExecutorService io = Executors.newFixedThreadPool(4)) {
CompletableFuture<String> details = CompletableFuture.supplyAsync(() -> sleepAndReturn(150, "Redmi Note 13"), io);
CompletableFuture<Integer> price = CompletableFuture.supplyAsync(() -> sleepAndReturn(100, "17999"), io)
.thenApply(Integer::parseInt);
CompletableFuture<String> page = details
.thenCombine(price, (name, p) -> name + " | Rs " + p)
.thenCompose(header -> CompletableFuture.supplyAsync(
() -> header + " | rating " + sleepAndReturn(50, "4.3"), io));
System.out.println(page.join());
CompletableFuture<String> failing = CompletableFuture
.supplyAsync(() -> { if (true) throw new IllegalStateException("offers service down"); return "10% off"; }, io)
.exceptionally(ex -> "no offers (" + ex.getCause().getMessage() + ")");
System.out.println(failing.join());
CompletableFuture<String> slow = CompletableFuture.supplyAsync(() -> sleepAndReturn(2000, "late"), io)
.completeOnTimeout("default recommendations", 200, TimeUnit.MILLISECONDS);
System.out.println(slow.join());
}
}
}Output
Redmi Note 13 | Rs 17999 | rating 4.3 no offers (offers service down) default recommendations
allOf
Example
Wait for many futures, then collect the results in order.
import java.util.*;
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
List<String> cities = List.of("Delhi", "Mumbai", "Chennai");
List<CompletableFuture<String>> weather = cities.stream()
.map(c -> CompletableFuture.supplyAsync(() -> c + ": " + (25 + c.length()) + "°C"))
.toList();
CompletableFuture.allOf(weather.toArray(new CompletableFuture[0])).join();
weather.forEach(f -> System.out.println(f.join()));
}
}Output
Delhi: 30°C Mumbai: 31°C Chennai: 32°C
Tip
Tip: Without an executor argument, supplyAsync uses the common ForkJoinPool, which is sized for CPU work. For blocking I/O (HTTP, DB), pass your own executor — or in Java 21, a virtual-thread executor.