40

I have a doubt about this code:

@Async
public CompletableFuture<String> doFoo() {
    CompletableFuture<String> fooFuture = new CompletableFuture<>();  

    try {
        String fooResult = longOp();
        fooFuture.complete(fooResult);
    } catch (Exception e) {
        fooFuture.completeExceptionally(e);
    }

    return fooFuture;
}

The question is: does doFoo return fooFuture only after longOp has finished (either correctly or exceptionally) and is therefore returning already completed futures or is Spring doing some magic and returning before executing the body? If the code is blocking on longOp(), how would you express that the computation is being fed to an executor?

Perhaps this? Any other way?

@Async
public CompletableFuture<String> doFoo() {

    CompletableFuture<String> completableFuture = new CompletableFuture<>();
    CompletableFuture.runAsync(() -> {
        try {
            String fooResult = longOp();
            completableFuture.complete(fooResult);
        } catch (Exception e) {
            completableFuture.completeExceptionally(e);
        }
    });
    return completableFuture;
}
Eddy
  • 1,662
  • 2
  • 21
  • 36

1 Answers1

54

Spring actually does all of the work behind the covers so you don't have to create the CompletableFuture yourself. Basically, adding the @Async annotation is as if you called your original method (without the annotation) like:

CompletableFuture<User> future = CompletableFuture.runAsync(() -> doFoo());

As for your second question, in order to feed it to an executor, you can specify the exectutor bean name in the value of the @Async annotation, like so:

    @Async("myExecutor")
    public CompletableFuture<User> findUser(String usernameString) throws InterruptedException {
        User fooResult = longOp(usernameString);
        return CompletableFuture.completedFuture(fooResult);
    }

The above would basically be the following as if you called your original method, like:

CompletableFuture<User> future = CompletableFuture.runAsync(() -> doFoo(), myExecutor);

And all of your exceptionally logic you would do with the returned CompletableFuture from that method.

Abdull
  • 26,371
  • 26
  • 130
  • 172
Dovmo
  • 8,121
  • 3
  • 30
  • 44
  • I see, so basically the @Async annotation is lifting my method into the CompletableFuture, exceptions automatically captured and tucked into the a completedExceptionally... ugh, I've done too much Scala lately not to get confused :D – Eddy Nov 17 '17 at 13:55
  • Yep, so `get()` will return your `String` result and/or propogate the exception up, or you can complete it `exceptionally` with a supplier – Dovmo Nov 17 '17 at 14:07
  • 1
    does it run even if i never .join() or .get()? – Dave Ankin Mar 03 '21 at 17:32
  • Does the method return type always have to be CompletableFuture or can we have Future as well? What are the advantages of the former – Jaison Varghese Aug 04 '21 at 06:06
  • 1
    @JaisonVarghese The return type for an `@Async` annotated method must be void, or a Future (so, a CompletableFuture works too). The CompletableFuture is a more recent API that provide more functionality than the Future (Java 8 vs Java 5, respectively). – FrenchFigaro Sep 24 '21 at 08:17
  • One question....is CompletableFuture must for @Async annotation? Does it not work with Custom Java data types ? – MiGo Nov 14 '22 at 18:35
  • 1
    Like @FrenchFigaro said, only void or Future is possible. Future is an interface and CompletableFuture one of the implementations. Custom Java data types can be wrapped in CompletableFuture i.e. CompletableFuture. CompletableFuture provides methods like join instead of using the method get where you have to put that in try..catch block. – John Dec 28 '22 at 17:15