What does anyOf() method do and when is it useful
Structured Java interview answer with junior, middle, and senior-level explanation.
π’ Junior Level
anyOf() β waits for the FIRST of the passed CompletableFutures to complete and returns its result.
CompletableFuture<String> fast = CompletableFuture.supplyAsync(() -> {
Thread.sleep(100);
return "Fast";
});
CompletableFuture<String> slow = CompletableFuture.supplyAsync(() -> {
Thread.sleep(1000);
return "Slow";
});
// Returns the result of the FIRST completed one
CompletableFuture<Object> any = CompletableFuture.anyOf(fast, slow);
any.thenAccept(result -> System.out.println(result)); // "Fast"
When to use:
- Query multiple sources β whoever responds first
- Fallback: primary and backup services
- Timeout via an alternative CF
π‘ Middle Level
How it works
// Query multiple servers
CompletableFuture<String> server1 = queryServerAsync("server1");
CompletableFuture<String> server2 = queryServerAsync("server2");
CompletableFuture<String> server3 = queryServerAsync("server3");
// Take the first response
CompletableFuture<Object> fastest = CompletableFuture.anyOf(server1, server2, server3);
fastest.thenAccept(response -> System.out.println("Got response: " + response));
Important: anyOf() returns CompletableFuture<Object> β you need to cast the type:
CompletableFuture.anyOf(cf1, cf2)
.thenAccept(result -> {
String str = (String) result; // cast is needed!
System.out.println(str);
});
Typical mistakes
- Unknown which CF completed:
CompletableFuture.anyOf(cf1, cf2).thenAccept(result -> { // result β Object, unclear which CF it came from // If types differ β need to check if (result instanceof String s) { } if (result instanceof Integer i) { } }); - Remaining CFs continue executing:
// anyOf does not cancel the remaining CFs! CompletableFuture.anyOf(fast, slow); // slow continues running in the background
π΄ Senior Level
Internal Implementation
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
return new OrRelay<>(cfs, null);
}
// When the first CF completes β the result is copied into OrRelay
// The remaining CFs are ignored (but continue executing)
Architectural Trade-offs
| anyOf() | allOf() |
|---|---|
| First completed | All completed |
| Object result | Void result |
| Fast fail | Full wait |
Edge Cases
1. Timeout pattern:
CompletableFuture<String> operation = fetchDataAsync();
CompletableFuture<Object> timeout = CompletableFuture
.delayedExecutor(5, TimeUnit.SECONDS)
.thenApply(v -> { throw new TimeoutException(); });
CompletableFuture<Object> result = CompletableFuture.anyOf(operation, timeout);
2. Multiple sources with fallback:
CompletableFuture<Data> primary = fetchFromPrimaryAsync();
CompletableFuture<Data> fallback = fetchFromCacheAsync();
CompletableFuture<Object> any = CompletableFuture.anyOf(primary, fallback);
any.thenAccept(result -> {
Data data = (Data) result;
// anyOf may have completed from fallback while primary is still running.
// isCompletedExceptionally() doesn't tell you WHO won the race.
// Reliable approach β compare result with known sources.
if (result == fallbackResult) {
log.warn("Primary failed or hasn't completed yet, using fallback");
}
});
3. Cancellation of remaining:
CompletableFuture<String> cf1 = fetchAsync(1);
CompletableFuture<String> cf2 = fetchAsync(2);
CompletableFuture<String> cf3 = fetchAsync(3);
CompletableFuture<Object> any = CompletableFuture.anyOf(cf1, cf2, cf3);
any.thenAccept(result -> {
// Cancel the rest
// cancel on an already completed CF is a no-op. But this code is misleading,
// as if all three are still running. Better to track the winner.
cf1.cancel(false);
cf2.cancel(false);
cf3.cancel(false);
});
Performance
anyOf():
- Creation: ~10 ns
- Overhead: ~5 ns per CF
- First completed β result
Cancellation of remaining CFs:
- cancel(false): ~100 ns
- But execution may continue
Production Experience
Resilient service call:
@Service
public class ResilientService {
public CompletableFuture<String> getData(String key) {
CompletableFuture<String> primary = primaryClient.getAsync(key);
CompletableFuture<String> secondary = secondaryClient.getAsync(key);
CompletableFuture<String> cache = cacheClient.getAsync(key);
return CompletableFuture.anyOf(primary, secondary, cache)
.thenApply(result -> (String) result);
}
}
When NOT to use anyOf
- Need ALL results β use
allOf - Fastest may come from an unreliable source β anyOf returns the first, not the best
Best Practices
// β
anyOf for fastest response
CompletableFuture.anyOf(cf1, cf2, cf3)
.thenAccept(result -> process((Type) result));
// β
Timeout via anyOf
CompletableFuture.anyOf(operation, timeoutCF);
// β
Cancel the rest on completion
any.thenAccept(r -> { cf1.cancel(false); cf2.cancel(false); });
// β anyOf when you need all results
// β Ignoring Object return type
// β Not cancelling remaining CFs
π― Interview Cheat Sheet
Must know:
- anyOf() returns the result of the FIRST completed CF
- Returns CompletableFuture
- Does not cancel remaining CFs β they continue executing
- Latency = min(all CFs) β fastest response
- Use case: fastest of multiple servers, fallback, timeout
Frequent follow-up questions:
- Does anyOf cancel remaining CFs? β No, need manual cancel(false)
- How to know which CF won? β Compare result with known sources
- anyOf vs applyToEither? β applyToEither for two CFs (typed), anyOf for N (Object)
- What if the first CF fails? β anyOf completes with an error. Need error handling
Red flags (DO NOT say):
- βanyOf returns a typed resultβ β returns Object, cast is needed
- βanyOf automatically cancels the restβ β no, they continue executing
- βanyOf guarantees the best resultβ β only fastest, not best
Related topics:
- [[9. What does allOf() method do and when to use it]]
- [[8. How to combine results of multiple CompletableFutures]]
- [[20. How to implement timeout for CompletableFuture]]
- [[16. How to properly execute multiple parallel requests to microservices]]