Definition
- allOf() – returns
CompletableFuturebecause input futures may have different types (String, Integer, BigDecimal). - Result collection – call
.join()on each individual future after allOf completes. - Fail-all –
allOf().join()throws if any future failed; reject entire operation for strict consistency. - Partial results – swallow allOf exception, check each
isCompletedExceptionally(), return what succeeded. - Fallbacks – chain
exceptionally(ex -> default)on each future before allOf; all futures always succeed. - Choice – fail-all for critical decisions (loan approval); fallbacks for dashboards and best-effort displays.
allOf() is like waiting for all 3 departments at National Bank of India to submit their reports. The "all done" signal tells you everyone finished but does not carry the reports. You must collect each one. If department C failed: fail-all rejects the batch ("all reports or nothing"), partial results submits 2 of 3, and fallback uses last month's report for the failed department.
Understanding the Concepts
Why does allOf() return Void?
The input futures may have different types: CF, CF, CF. Java cannot express a heterogeneous tuple, so allOf returns Void and you collect results individually.
CF<Integer> balance = supplyAsync(() -> getBalance("SAV2001"));
CF<Integer> score = supplyAsync(() -> getCreditScore("SAV2001"));
CF<BigDecimal> property = supplyAsync(() -> getPropertyValue("P001"));
CompletableFuture<Void> allDone = CompletableFuture.allOf(balance, score, property);
allDone.join(); // waits for all, returns null
int b = balance.join(); // already done
int s = score.join(); // already done
BigDecimal p = property.join(); // already done
Strategy 1: Fail-all
If any future fails, allOf().join() throws. The entire operation is rejected.
try {
CompletableFuture.allOf(balance, score, property).join();
return decide(balance.join(), score.join(), property.join());
} catch (CompletionException e) {
return "REJECTED: " + e.getCause().getMessage();
}
Strategy 2: Partial results
Swallow the allOf exception, check each future individually, return what succeeded.
CompletableFuture.allOf(balance, score, property)
.exceptionally(ex -> null) // swallow
.join();
Map<String, Object> results = new HashMap<>();
if (!balance.isCompletedExceptionally()) results.put("balance", balance.join());
if (!score.isCompletedExceptionally()) results.put("score", score.join());
if (!property.isCompletedExceptionally()) results.put("property", property.join());
Strategy 3: Fallbacks (most resilient)
Chain exceptionally() on each future before allOf. Every future always succeeds.
CF<Integer> safeBalance = balance.exceptionally(ex -> 0);
CF<Integer> safeScore = score.exceptionally(ex -> 500);
CF<BigDecimal> safeProperty = property.exceptionally(ex -> BigDecimal.ZERO);
CompletableFuture.allOf(safeBalance, safeScore, safeProperty).join();
// All always succeed with real or fallback values
When to use each strategy?
| Strategy | Use case | Trade-off |
|---|---|---|
| Fail-all | Loan decision (needs all real data) | Higher error rate |
| Partial | Dashboard (show what's available) | Incomplete data |
| Fallback | Resilient display (cached defaults) | May show stale data |
How do you collect homogeneous results?
When all futures have the same type, use a stream.
List<CF<String>> futures = accountIds.stream()
.map(id -> supplyAsync(() -> fetchBalance(id)))
.toList();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
List<String> results = futures.stream()
.map(CompletableFuture::join)
.toList();
What are common interview follow-up questions?
- Why not return a tuple? Java does not have tuples. allOf would need a heterogeneous container.
- Does allOf wait for all even if one fails? Yes, allOf waits for all to complete (success or failure) before completing.
- Can you use anyOf for fail-fast? Yes, anyOf completes when the first future completes (success or failure).
Visual Overview
allOf(cf1, cf2, cf3)
│
▼
Wait for ALL to complete
│
▼
CF<Void> (no results!)
│
▼
Collect individually:
cf1.join() → value1
cf2.join() → value2
cf3.join() → value3
Failure Strategies:
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Fail-all │ │ Partial │ │ Fallback │
│ Any fails │ │ Skip failed│ │ Default for│
│ = reject │ │ = partial │ │ failed │
└────────────┘ └────────────┘ └────────────┘
Key Takeaway
allOf() returns CF
Fail-all is for strict consistency where all data is required: loan approval decisions. Fallbacks are for resilient displays where stale or default data is acceptable: dashboards showing cached scores when the live service is down.
At National Bank of India, the loan dashboard loads three data sources. The credit bureau is down 5% of the time. With fail-all, the dashboard shows an error 5% of the time. With fallbacks using cached scores, the dashboard always loads.
Remember one sentence: allOf returns Void so you must join each future individually, and chain exceptionally before allOf for resilient fallbacks.
---