javaconcurrencycompletable-futurejava.util.concurrent

Is CompletableFuture.supplyAsync() redundant in case it does not modify the incoming value?


During a course on concurrent programming, I've run into this sample code:

@Test
void promiseTestCompose2() throws Exception {
  CompletableFuture<Integer> future1 = CompletableFuture
    .supplyAsync(this::slowInit)
    .thenApply(this::slowIncrement);

  CompletableFuture<Integer> thenCompose = future1
    .thenCompose(value -> CompletableFuture.supplyAsync(() -> value))
    .thenApply(this::slowIncrement);

  int result = thenCompose.get();
  assertEquals(result, 3);
}

It looks like in this part, the thenCompose() clause is redundant:

CompletableFuture<Integer> thenCompose = future1
  .thenCompose(value -> CompletableFuture.supplyAsync(() -> value))
  .thenApply(this::slowIncrement);

for if I change it to

CompletableFuture<Integer> thenCompose = future1.thenApply(this::slowIncrement);

the test still passes.

So my question is whether .thenCompose(value -> CompletableFuture.supplyAsync(() -> value)) is indeed redundant here or am I missing something?

I would like an explanation on whether I can remove thenCompose() in case it doesn't modify incoming value.


Solution

  • The call seems redundant at first glance, but it could be a poor attempt at trying to ensure that thenApply(this::slowIncrement) will not be executed on the current thread (which would happen if future1 is already completed), or the same thread as the call which returned future1 (which could be the current thread or the thread that ran slowInit()).

    The proper way to do that would be to use thenApplyAsync() instead:

    CompletableFuture<Integer> thenCompose = future1.thenApplyAsync(this::slowIncrement);