Reputation: 23
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?
Upvotes: 2
Views: 2092
Reputation: 120848
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.
Upvotes: 2