简体   繁体   中英

CompletableFuture callback not called if completed manually

In this basic example of CompletableFuture , I run a task asynchronously and when it's finished, an async callback should be triggered.

One second after I start running the task, and before it's finished, I complete it. After that I don't see it running the asynchronous callback anymore.

public static void main(String[] args) throws InterruptedException {
    runTask();
    Thread.sleep(1000);
    completableFuture.complete("Test");
    Thread.sleep(4000);
}

public static void runTask() {
    completableFuture = CompletableFuture.supplyAsync(() -> {
        System.out.println("Running...");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("...finished");
        return "finished task";
    })
    .thenApplyAsync(s -> {
        System.out.println("Apply on result: " + s);
        return "Result: " + s;
    })
}

The result of this is:

Running...
...finished

The thing is that if I add another callback, then it runs the first one but not the second one.

.thenApplyAsync(s -> {
    System.out.println("Second apply with result: " + s);
    return "Result: " + s;
})

Then the result is:

Running...
...finished
Apply on result: finished task

Reading the documentation I understood all the callbacks would be always called, even if the future is completed manually. Am I missing something here?

I guess if you write it slightly differently, it should make some sense:

public static void runTask() {

    CompletableFuture<String> one = CompletableFuture.supplyAsync(() -> {
        System.out.println("Running...");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("...finished");
        return "finished task";
    });

    CompletableFuture<String> two = one.thenApplyAsync(s -> {
        System.out.println("Apply on result: " + s);
        return "Result: " + s;
    });

    completableFuture = two;
} 

So, one in your case starts just fine, but before two can even start, you issue completableFuture.complete("Test"); . So when one is done, there is nothing for it to thenApplyAsync , since that one is already completed.

When you add one more stage, you basically get:

....
CompletableFuture<String> two = one.thenApplyAsync(s -> {
     System.out.println("Apply on result: " + s);
     return "Result: " + s;
});

CompletableFuture<String> three = two.thenApplyAsync(s -> {
     System.out.println("Second apply with result: " + s);
     return "Result: " + s;
});

completableFuture = three;

You can probably see what happens here, without even me explaining.


To that end, I can't see where the documentation would make this clear though. I guess we somehow need to see that in package documentation , via:

When two or more threads attempt to complete, completeExceptionally, or cancel a CompletableFuture, only one of them succeeds.

This somehow implies that if a certain stage is not yet started, but someone else, externally complete s it; that stage will not run at all. This makes sense, but the package documentation could be more clear, imo.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM