CompletableFuture.supplyAsync vs new CompletableFuture()

I don't understand what's happening here with CompletableFuture/supplyAsync.

If I invoke the supplyAsync method from a CompletableFuture object previously instantiated, it never completes:

public static void ex1() throws ExecutionException, InterruptedException {
   final CompletableFuture<String> cf = new CompletableFuture<>();
   cf.supplyAsync(() -> {
      System.out.println("Main.m3");
      return "Main";
   });
   System.out.println("Start: cf = " + cf);
   final String value = cf.get();
   System.out.println("End: value = " + value);
}

This is the output:

Start: cf = java.util.concurrent.CompletableFuture@5b480cf9[Not completed]
Main.m3

As you can see the System.out.println("End: cf = " + cf); is never executed.

To make it running I need to put a complete(my value) inside the body of the supplyAsync.

But if I directly invoke the supplyAsync while creating the CompletableFuture (or by calling the static CompletableFuture.supplyAsync):

public static void ex2() throws ExecutionException, InterruptedException {
   final CompletableFuture<String> cf = new CompletableFuture<>()
      .supplyAsync(() -> {
         System.out.println("Main.m3");
         return "Main";
      });
   System.out.println("Start: cf = " + cf);
   final String value = cf.get();
   System.out.println("End: value = " + value);
}

It works as expected. You can see here the output:

Start: cf = java.util.concurrent.CompletableFuture@5b480cf9[Not completed]
Main.m3
End: value = Main

So my question is: why this? Is there something I am missing?

Upvotes: 2

Views: 4813

Answers (2)

AlexC
AlexC

Reputation: 1415

java.util.concurrent.CompletableFuture#supplyAsync(java.util.function.Supplier<U>) is a static function, so in ex1 you are allocating a CompletableFuture but never using it.

From java 8 source:

/**
 * Returns a new CompletableFuture that is asynchronously completed
 * by a task running in the {@link ForkJoinPool#commonPool()} with
 * the value obtained by calling the given Supplier.
 *
 * @param supplier a function returning the value to be used
 * to complete the returned CompletableFuture
 * @param <U> the function's return type
 * @return the new CompletableFuture
 */
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
    return asyncSupplyStage(ASYNC_POOL, supplier);
}

in your case ex1 should be:

public static void ex1() throws ExecutionException, InterruptedException {
    CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
        System.out.println("Main.m3");
        return "Main";
    });
    System.out.println("Start: cf = " + cf);
    final String value = cf.get();
    System.out.println("End: value = " + value);
}

and ex2 is the correct format but you get there by accident, by allocating a new CompletableFuture and calling a static method, but in ex2 you do not discard the returned value while in ex1 you do.

In you ex1 and ex2 you are effectively calling the same method, the code I provided above for ex1 is correct for both cases where you call static correctly using class name and not discarding the returned object.

Upvotes: 2

payloc91
payloc91

Reputation: 3809

supplyAsync is a static method that returns a new CompletableFuture and it should not be called via an instance.

In the first case, you are calling get() on a CompletableFuture that never started anything.

In fact, you'll notice that your program when calling ex1() will remain in a pending state forever. And because of this, it can never execute the next line, which prints the result Main (of the other Future that computed in parallel).

The point, is that you do not store the second CompletableFuture anywhere. So there is no way you'll be able to call the correct get().


In your second case you are building the instance with the return value of supplyAsync, and you store it in cf. And this is the right way to construct a CompletableFuture. (the new CompletableFuture<>() part is superfluous; in fact, you assign it a new instance right away with supplyAsync).

When you call get() you therefore wait for the right CompletableFuture to return its result.

Upvotes: 4

Related Questions