Back to CompletableFuture & Resilience
Q14Question 14 of 19

How to use allOf() correctly?

Key Concepts
  • allOf() returns CF<Void> because the input futures may have different types; it signals completion but carries no results.
  • You must call join() on each individual future to collect results after allOf() completes.
  • Fail-all strategy: allOf().join() throws if any future failed; reject the entire operation.
  • Partial results strategy: swallow allOf exception, check each future individually, return what succeeded.
  • Fallback strategy: chain exceptionally() on each future before allOf(); all always succeed with real or default values.
  • Choose the strategy based on consistency requirements: strict (fail-all) vs resilient (fallbacks).

Definition

allOf() returns CF<Void> that signals when all futures complete but carries no results; you must join each future individually and choose a failure strategy: fail-all, partial results, or fallbacks.
  • allOf() – returns CompletableFuture because 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.

JAVA
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.

JAVA
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.

JAVA
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.

JAVA
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.

JAVA
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

DIAGRAM
  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 because the input futures may have different types. It signals "all done" but carries no results. You must join each future individually to collect values. When one future fails, choose a strategy: fail-all (reject the entire operation), partial results (return what succeeded), or fallbacks (chain exceptionally on each future before allOf so all always succeed).

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.

---

Share
Use ← → arrow keys to navigate