// Simple async execution
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(1000);
        return "Hello";
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
});

// Transform result
CompletableFuture<Integer> length = future.thenApply(String::length);

// Chain operations
CompletableFuture<String> chained = future
    .thenApply(s -> s + " World")
    .thenApply(String::toUpperCase);

// Combine two futures
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<String> combined = future1.thenCombine(
    future2,
    (s1, s2) -> s1 + " " + s2
);

// Wait for all
List<CompletableFuture<String>> futures = Arrays.asList(
    CompletableFuture.supplyAsync(() -> "Task 1"),
    CompletableFuture.supplyAsync(() -> "Task 2"),
    CompletableFuture.supplyAsync(() -> "Task 3")
);
CompletableFuture<Void> allDone = CompletableFuture.allOf(
    futures.toArray(new CompletableFuture[0])
);

// Exception handling
CompletableFuture<String> safe = future
    .exceptionally(ex -> "Error: " + ex.getMessage())
    .handle((result, ex) -> {
        if (ex != null) {
            return "Handled: " + ex.getMessage();
        }
        return result;
    });

// Get result (blocking)
try {
    String result = future.get();
    // or with timeout
    String result2 = future.get(5, TimeUnit.SECONDS);
} catch (Exception e) {
    e.printStackTrace();
}