Question 9 Β· Section 19

What does allOf() method do and when to use it

Structured Java interview answer with junior, middle, and senior-level explanation.

Language versions: English Russian Ukrainian

🟒 Junior Level

allOf() β€” waits for ALL passed CompletableFutures to complete and returns a new CompletableFuture.

CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> "!");

// Waits for all three to complete
CompletableFuture<Void> all = CompletableFuture.allOf(cf1, cf2, cf3);

all.thenRun(() -> {
    // All completed β€” can now get results
    System.out.println(cf1.join() + " " + cf2.join() + " " + cf3.join());
    // Hello World !
});

Important: allOf() returns CompletableFuture<Void> β€” it does not contain the results, you need to call join() on each original CF.


🟑 Middle Level

How it works

CompletableFuture<User> user = getUserAsync(userId);
CompletableFuture<Order> order = getOrderAsync(userId);
CompletableFuture<Preferences> prefs = getPrefsAsync(userId);

// Wait for all
CompletableFuture<Void> all = CompletableFuture.allOf(user, order, prefs);

// After all complete
all.thenRun(() -> {
    User u = user.join();    // does not block β€” already ready
    Order o = order.join();
    Preferences p = prefs.join();

    process(new UserProfile(u, o, p));
});

Error handling

// If at least one fails β€” allOf will also fail
CompletableFuture.allOf(cf1, cf2, cf3)
    .exceptionally(ex -> {
        // ex β€” CompletionException with cause
        log.error("One of the CFs failed", ex);
        return null;
    });

Typical mistakes

  1. Trying to get results from allOf: ```java // ❌ allOf returns Void! CompletableFuture all = CompletableFuture.allOf(cf1, cf2); // all.join(); // null!

// βœ… Need join on each CF CompletableFuture.allOf(cf1, cf2).thenRun(() -> { String r1 = cf1.join(); String r2 = cf2.join(); });


---

## πŸ”΄ Senior Level

### Internal Implementation

```java
// Internally allOf creates a dependency tree and completes
// when all passed CFs are done. Real internal JDK class names differ β€”
// this is a conceptual description.

Architectural Trade-offs

allOf() thenCombine()
Many CFs (2+) Two CFs
Returns Void Returns result
Need join for results Results in callback

Edge Cases

1. Collect results pattern:

public CompletableFuture<List<User>> getUsers(List<Long> ids) {
    List<CompletableFuture<User>> futures = ids.stream()
        .map(id -> getUserAsync(id))
        .toList();

    return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
        .thenApply(v ->
            futures.stream()
                .map(CompletableFuture::join)
                .toList()
        );
}

2. Partial results with timeout:

CompletableFuture<Void> all = CompletableFuture.allOf(cf1, cf2, cf3);

all.orTimeout(5, TimeUnit.SECONDS)
   .exceptionally(ex -> {
       // Timeout or error
       // But some CFs may have completed
       if (cf1.isDone()) System.out.println("cf1 done");
       if (cf2.isDone()) System.out.println("cf2 done");
       return null;
   });

Performance

allOf():
- Creation: ~10 ns
- Overhead per CF: ~5 ns
- For N CFs β€” tree of depth log(N)

join() on a ready CF: ~1 ns (does not block)

Production Experience

Batch API calls:

@Service
public class DashboardService {
    public CompletableFuture<Dashboard> getDashboard(Long userId) {
        CompletableFuture<User> user = userService.findByIdAsync(userId);
        CompletableFuture<List<Order>> orders = orderService.findByUserIdAsync(userId);
        CompletableFuture<List<Notification>> notifications =
            notificationService.findByUserIdAsync(userId);
        CompletableFuture<Stats> stats = statsService.getStatsAsync(userId);

        return CompletableFuture.allOf(user, orders, notifications, stats)
            .thenApply(v -> new Dashboard(
                user.join(),
                orders.join(),
                notifications.join(),
                stats.join()
            ));
    }
}

Best Practices

// βœ… allOf for parallel independent calls
CompletableFuture.allOf(cf1, cf2, cf3)
    .thenRun(() -> combine(cf1.join(), cf2.join(), cf3.join()));

// βœ… Error handling
CompletableFuture.allOf(cf1, cf2)
    .exceptionally(ex -> handleErrors(ex, cf1, cf2));

// ❌ allOf when sequential is needed
// ❌ Ignoring that allOf returns Void

When NOT to use allOf

  • Need results as they become ready β€” process each CF separately
  • One failure should not cancel the rest β€” allOf throws on the first error
// ❌ allOf β€” one error brings everything down
CompletableFuture.allOf(cf1, cf2, cf3)
    .exceptionally(ex -> null);  // all results lost

// βœ… Handle each CF separately
cf1.handle((r, ex) -> ex != null ? fallback1 : r);
cf2.handle((r, ex) -> ex != null ? fallback2 : r);
cf3.handle((r, ex) -> ex != null ? fallback3 : r);

🎯 Interview Cheat Sheet

Must know:

  • allOf() accepts N CompletableFutures, returns CompletableFuture
  • Completes when ALL CFs are done, or on the first error
  • To get results, call join() on each original CF
  • Latency = max(all CFs) β€” all execute in parallel
  • Pattern allAsList: allOf + stream.map(CompletableFuture::join).toList()

Frequent follow-up questions:

  • Does allOf return results? β€” No, Void. join() on each CF separately
  • What if one CF fails? β€” allOf completes with a CompletionException
  • Does allOf block? β€” No, it returns a CF. allOf().join() blocks
  • How to get partial results on error? β€” Check isDone() on each CF

Red flags (DO NOT say):

  • β€œallOf returns an array of results” β€” it returns Void
  • β€œallOf continues when one CF fails” β€” it completes with an error
  • β€œallOf().join() is non-blocking” β€” join() blocks until completion

Related topics:

  • [[10. What does anyOf() method do and when is it useful]]
  • [[8. How to combine results of multiple CompletableFutures]]
  • [[16. How to properly execute multiple parallel requests to microservices]]
  • [[6. How to handle exceptions in CompletableFuture chain]]