Back to Blog
Java

Java CompletableFuture anyOf: Waiting for the First Result

java completablefuture anyof: Learn how to use CompletableFuture.anyOf to wait for the first completed future, handle its result, and manage exceptions in Java async p...

CompletableFutureAsync ProgrammingConcurrencyJavaFuture
Illustration of multiple asynchronous tasks racing, with the first one highlighted as the winner in a Java CompletableFuture anyOf operation.

When you have several asynchronous tasks and you only need the first one to complete, CompletableFuture.anyOf provides a straightforward way to wait for that event. In Java, java completablefuture anyof is a static method that takes an array of CompletableFuture<?> and returns a new CompletableFuture<Object> that completes when any of the input futures complete, carrying the result of the first one to finish.

What CompletableFuture.anyOf Does

The method signature is:

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

It accepts a varargs array of CompletableFuture instances. The returned future completes as soon as any one of the supplied futures completes. If that future completes normally, the result is the value of that future. If it completes exceptionally, the returned future completes exceptionally with the same exception. If multiple futures complete at the same time, the result is nondeterministic—any one of them may be selected.

The key point is that anyOf does not wait for all futures. It reacts to the first terminal state (normal or exceptional) among the input futures. This is useful when you want to race several independent operations and proceed with the first available outcome.

A Minimal anyOf Example

Consider a scenario where you query two different services and only need the fastest response. The following code demonstrates the basic usage:

import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; public class AnyOfExample { public static void main(String[] args) { CompletableFuture<String> serviceA = CompletableFuture.supplyAsync(() -> { sleep(200); return "Result from A"; }); CompletableFuture<String> serviceB = CompletableFuture.supplyAsync(() -> { sleep(100); return "Result from B"; }); CompletableFuture<Object> first = CompletableFuture.anyOf(serviceA, serviceB); // Block and get the result (for demonstration; in real code use thenAccept) Object result = first.join(); System.out.println("First result: " + result); } private static void sleep(int millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }

Here, serviceB completes after 100 ms, so first completes with the value "Result from B". The join() call blocks the main thread, which is fine for a simple test. In a real application you would attach a callback instead of blocking.

Extracting the Result from anyOf

Because anyOf returns a CompletableFuture<Object>, the result type is erased to Object. You cannot directly obtain a typed value without a cast. The cast is safe only if you know which future is likely to complete first and what type it returns. In the previous example, both futures return String, so casting to String is straightforward:

String result = (String) first.join();

If the futures can return different types, you may need to inspect the result or use a common supertype. A more robust approach is to chain a thenApply that performs the cast and handles the type mismatch:

first.thenApply(res -> { if (res instanceof String) { return (String) res; } else { return res.toString(); } });

This avoids a blind cast and gives you a chance to normalize the result. Keep in mind that the cast is a runtime operation; the compiler cannot verify the type because anyOf deliberately erases it.

Handling Exceptions in anyOf

If the first future to complete fails exceptionally, the anyOf future also completes exceptionally. The exception is the same one thrown by the failed future. This behavior is important because it means you cannot assume that a successful completion of anyOf implies that all futures succeeded—only that the first one did.

Consider this example:

CompletableFuture<Integer> failing = CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Service failed"); }); CompletableFuture<Integer> slow = CompletableFuture.supplyAsync(() -> { sleep(500); return 42; }); CompletableFuture<Object> first = CompletableFuture.anyOf(failing, slow); first.exceptionally(ex -> { System.out.println("First future failed: " + ex.getMessage()); return -1; }).join();

Here, failing completes exceptionally immediately, so first completes exceptionally with the RuntimeException. The exceptionally callback handles it. If slow had completed first, first would have completed normally with 42.

When you use anyOf in a pipeline, you should attach exception handling early so that failures are not silently swallowed. Use exceptionally, handle, or whenComplete to react to the exceptional completion.

anyOf vs. allOf: Choosing the Right Combinator

CompletableFuture.allOf waits for all futures to complete, while anyOf waits for the first. The choice depends on your coordination requirement. The table below summarizes the key differences:

AspectanyOfallOf
CompletionFirst future completesAll futures complete
Result typeCompletableFuture<Object>CompletableFuture<Void>
Result valueResult of the first completed futureNo value; you must query each future
ExceptionPropagates exception from first failurePropagates exception from any failure
Use caseRace, fastest response, first successAggregate results, parallel fan-out

Use anyOf when you need the first available result and can ignore the others. Use allOf when you must wait for every task to finish before proceeding. For example, a dashboard that displays data from multiple sources needs allOf, whereas a failover mechanism that tries several endpoints and accepts the first successful response fits anyOf.

Cancellation and Resource Considerations

anyOf does not cancel the remaining futures when the first one completes. The other tasks continue to run in the background. This is a critical operational detail. If you are racing tasks that consume significant resources, you may want to cancel the losers explicitly. However, cancellation is cooperative: the future's cancel method only sets a flag; the underlying task must check the interruption status to stop.

You can cancel the other futures after anyOf completes by storing them in a list and calling cancel on each. For example:

List<CompletableFuture<String>> futures = List.of(serviceA, serviceB); CompletableFuture<Object> first = CompletableFuture.anyOf(futures.toArray(new CompletableFuture[0])); first.whenComplete((res, ex) -> { futures.forEach(f -> f.cancel(true)); });

This cancels all futures once first completes, which is useful when you no longer need the other results. Be aware that cancel(true) may interrupt the thread if the task is running, but it does not guarantee immediate termination. For tasks that are not interruptible, they will continue until they finish.

Another consideration is thread pool usage. If each future uses a separate thread from a shared pool, the uncompleted tasks will keep those threads occupied until they finish. In high-concurrency scenarios, this can exhaust the pool. Design your tasks to be short-lived or use a dedicated pool for the race.

When Not to Use anyOf

anyOf is not a universal solution for all asynchronous coordination. Avoid it when you need to combine results from multiple futures into a single value; allOf or a custom combination is more appropriate. Also avoid it when the type of the result is critical and cannot be safely cast. The loss of type information is a real tradeoff.

If you need to wait for the first future that completes successfully, ignoring failures, anyOf alone is not sufficient. You must attach exceptionally to each input future to convert failures into a sentinel value, or use a different pattern such as CompletableFuture.anyOf with handle on each future. For example, you could map each future to a Optional that is empty on failure, then use anyOf on those mapped futures. This gives you a typed result and lets you filter out failures.

Finally, be cautious when using anyOf with futures that may never complete. If none of the input futures ever finish, the anyOf future will never complete, potentially causing a hang. Always apply timeouts to the individual futures or to the combined future using orTimeout (available since Java 9) to enforce a deadline.

CompletableFuture<Object> first = CompletableFuture.anyOf(serviceA, serviceB) .orTimeout(2, TimeUnit.SECONDS);

This ensures that the combined future completes exceptionally with a TimeoutException if no input finishes within two seconds. Timeouts are essential in production to prevent indefinite waits.

java completablefuture anyof: Practical Usage and Code Examp | RYUSLOG DEV