javaintermediate

CompletableFuture — Async Programming

Chain async operations with CompletableFuture: thenApply, thenCompose, allOf, and exception handling.

java
import java.util.concurrent.*;
import java.util.List;
import java.util.stream.Collectors;

public class AsyncDemo {
    static ExecutorService executor = Executors.newFixedThreadPool(4);

    static CompletableFuture<String> fetchUser(int id) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(100); // simulate network call
            return "User-" + id;
        }, executor);
    }

    static CompletableFuture<List<String>> fetchOrders(String user) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(100);
            return List.of(user + "-Order1", user + "-Order2");
        }, executor);
    }

    public static void main(String[] args) throws Exception {
        // Chain: fetch user then orders
        CompletableFuture<List<String>> result = fetchUser(1)
            .thenCompose(user -> fetchOrders(user));

        System.out.println(result.get()); // [User-1-Order1, User-1-Order2]

        // Transform result
        CompletableFuture<String> upper = fetchUser(2)
            .thenApply(String::toUpperCase);

        // Combine two futures
        CompletableFuture<String> combined = fetchUser(1)
            .thenCombine(fetchUser(2), (u1, u2) -> u1 + " & " + u2);
        System.out.println(combined.get());

        // Run all in parallel
        List<CompletableFuture<String>> futures = List.of(
            fetchUser(1), fetchUser(2), fetchUser(3)
        );
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        List<String> users = futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
        System.out.println(users);

        // Error handling
        fetchUser(-1)
            .thenApply(String::toUpperCase)
            .exceptionally(ex -> "Fallback: " + ex.getMessage())
            .thenAccept(System.out::println);

        executor.shutdown();
    }

    static void sleep(long ms) {
        try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
    }
}

Use Cases

  • Parallel API calls to multiple services
  • Non-blocking data processing pipelines
  • Combining results from independent async tasks

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.