Guy Grin
Guy Grin

Reputation: 2034

Nested Futures not executing

I encountered a strange situation. I'm fiddling with CompletableFuture and when running the following code I have unexpected results:

public static void main(String[] args) {     
    CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<Object>>>>>> completableFutureCompletableFuture = CompletableFuture.supplyAsync(() -> {
        System.out.println("first");
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("second");
            return CompletableFuture.supplyAsync(() -> {
                System.out.println("third");
                return CompletableFuture.supplyAsync(() -> {
                    System.out.println("fourth");
                    return CompletableFuture.supplyAsync(() -> {
                        System.out.println("fifth");
                        return CompletableFuture.completedFuture(null);
                    });
                });
            });
        });
    });

   completableFutureCompletableFuture.get();
}

No exception is thrown (even when using exceptionally) and what I see is that the console output is

first
second
third // appears sometimes

Now, obviously this code has no real production value but this is a representation of a case where your code has an unknown number of nestings where each, or some of them, create CompleteableFutures which won't be executed.

Any explanation (and example on how to fix) would be greatly appreciated

Upvotes: 5

Views: 2754

Answers (3)

Jeremy Grand
Jeremy Grand

Reputation: 2370

It happens because your CompletableFuture are executed asynchronously but your program terminates before the fifth call happens (I assume you ran it in a single main and returned just after creating your futures).

As you may not know how many Future are stacked in your Future (due to type erasure). You may want to perform a recursive .get().

See :

public static void main(String[] args) throws InterruptedException, ExecutionException {

    CompletableFuture<?> futures = getFutures();
    recursiveGet(futures);
    System.out.println("finished");

}

public static CompletableFuture<?> getFutures() {
    CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<Object>>>>>> compositeCompletable = CompletableFuture.supplyAsync(() -> {
        System.out.println("first");
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("second");
            return CompletableFuture.supplyAsync(() -> {
                System.out.println("third");
                return CompletableFuture.supplyAsync(() -> {
                    System.out.println("fourth");
                    return CompletableFuture.supplyAsync(() -> {
                        System.out.println("fifth");
                        return CompletableFuture.completedFuture(null);
                    });
                });
            });
        });
    });
    return compositeCompletable;
}

public static void recursiveGet(Future<?> future) throws InterruptedException, ExecutionException{
    Object result = future.get();
    if(result instanceof Future){
        recursiveGet((Future<?>) result);
    }
}

which returns

first
second
third
fourth
fifth
finished

Upvotes: 5

Liviu Stirb
Liviu Stirb

Reputation: 6075

Just tested this and it works. I think the reason why is not working for you is because you run in in a main method and you did not wait to complete. I did a Thread.sleep(1000) after your code and it worked. The best way would be to wai for termination: completableFutureCompletableFuture.get().get().get().get().get()

Upvotes: 3

john16384
john16384

Reputation: 8044

The reason why this doesn't work is because in your simple test the VM exits before all tasks are completed.

When you call completableFutureCompletableFuture.get() only the first nesting of the futures is guaranteed to have finished. The VM exits, and all threads get killed.

In other words, the first nested future could still be "uncompleted" as its thread might still be busy. However, when you try to get its result with get it will of course wait until it completed and it will work as expected. Just try:

completableFutureCompletableFuture.get().get().get().get().get()

... then you force all futures to have completed and everything works as expected.

Upvotes: 8

Related Questions