javaconcurrencycompletable-future

CompletableFuture: like anyOf() but return a new CompletableFuture that is completed when any of the given CompletableFutures return not null


I have multiple CompletableFuture

CompletableFuture<String> comp1 = CompletableFuture.supplyAsync(() -> ...);
CompletableFuture<String> comp2 = CompletableFuture.supplyAsync(() -> ...);
...
CompletableFuture<String> compN = CompletableFuture.supplyAsync(() -> ...);

I need something like

CompletableFuture<Object> firstResult = CompletableFuture.anyOf(comp1, comp2, ..., compN);

but this returns the result from the first one that completes.

I want to have something that returns the first non-null result from the first one that completes. Is it possible?


Solution

  • There is no helper function in CompletableFuture that returns first future to complete with a non-null value.

    You can implement your own:

    public static <T> CompletableFuture<T> anyNotNull(Collection<CompletableFuture<? extends T>> cfs) {
        var cfsCopy = new ArrayList<>(cfs);
    
        if (cfsCopy.isEmpty()) {
            return CompletableFuture.failedFuture(new NoSuchElementException("No futures provided"));
        }
    
        var result = new CompletableFuture<T>();
        var remaining = new AtomicInteger(cfsCopy.size());
    
        for (CompletableFuture<? extends T> cf : cfsCopy) {
            cf.whenComplete((v, ex) -> {
                if (result.isDone()) return;
                if (Objects.nonNull(v)) {
                    result.complete(v);
                    return;
                }
                if (remaining.decrementAndGet() == 0) {
                    result.completeExceptionally(new NoSuchElementException("No future completed with a non-null value"));
                }
            });
        }
    
        return result;
    }
    

    Few notes about the code:

    Possible changes of the code that could make sense. Some of them are inspired by implementation of CompletableFuture.anyOf: