What is the difference between thenApply and thenApplyAsync of Java CompletableFuture?
The difference has to do with the Executor
that is responsible for running the code. Each operator on CompletableFuture
generally has 3 versions.
thenApply(fn)
- runsfn
on a thread defined by theCompleteableFuture
on which it is called, so you generally cannot know where this will be executed. It might immediately execute if the result is already available.thenApplyAsync(fn)
- runsfn
on a environment-defined executor regardless of circumstances. ForCompletableFuture
this will generally beForkJoinPool.commonPool()
.thenApplyAsync(fn,exec)
- runsfn
onexec
.
In the end the result is the same, but the scheduling behavior depends on the choice of method.
You're misunderstanding the examples you quoted. In both examples, the second function has to wait for the first function to complete. Whenever you call a.then___(b -> ...)
, input b
is the result of a
and has to wait for a
to complete, regardless of whether you use Async or not.
The actual example in the article is
CompletableFuture<String> receiver = CompletableFuture.supplyAsync(this::findReceiver);
receiver.thenApplyAsync(this::sendMsg);
receiver.thenApplyAsync(this::sendMsg);
Notice the thenApplyAsync
both applied on receiver
, not chained in the same statement. This means both function can start once receiver
completes, in an unspecified order. (Any assumption of order is implementation dependent.)
More technical explanation
I must point out that thenApply
and thenApplyAsync
are terribly named and are confusing to the unfamiliar. There is nothing in thenApplyAsync
that is more asynchronous than thenApply
from the contract of these methods.
The difference between the two has to do with on which thread the function is run. The function supplied to thenApply
may run on any of the threads that
- call
complete
- call
thenApply
on the same instance
while thenApplyAsync
either uses a default Executor
(a.k.a. thread pool), or a supplied Executor
.
Asynchrony != threads
thenApply
/thenApplyAsync
, and their counterparts thenCompose
/thenComposeAsync
, handle
/handleAsync
, thenAccept
/thenAcceptAsync
, are all asynchronous! The asynchronous nature of these function has to do with the fact that an asynchronous operation eventually calls complete
or completeExceptionally
. The idea came from Javascript, which is indeed asynchronous but isn't multi-threaded.