简体   繁体   中英

Stop CompletableFuture chaining inside the Supplier

I have such an operation,

public void createFuture() {
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 5);
    future.thenApply(i -> {
        if (i == 5) {
            System.out.println("hello, i equals to 5 so expensive operations are unnecessary");
        }
        return i;
    }).thenApply(i -> {
        if (i != 5) {
            System.out.println("Expensive operation");
        }
        return i;
    }).thenApply(i -> {
        if (i != 5) {
            System.out.println("Expensive operation");
        }
        return i;
    }).thenApply(i -> {
        if (i != 5) {
            System.out.println("Expensive operation");
        }
        return i;
    }).thenApply(i -> {
        if (i != 5) {
            System.out.println("Expensive operation");
        }
        return i;
    });

}

Here in the first block, I check some condition (i == 5) and if it is true, I don't need the rest of the operations. I don't want to throw an exception to cancel the rest because this is not an exceptional situation. Is there a nice way to do it other than passing some boolean to each operation?

CompletableFuture does not have mechanisms to support conditional statements. In your case the code can be simplified by extracting all tasks for i != 5 into one CompletableFuture with thenCompose .

Other workaround would be to create a map, filter it and create a completable future with it:

Map<Predicate<Integer>, Runnable> m = Map.of(
    integer -> integer == 5,
    () -> System.out.println("Task 1"),
    integer -> integer != 5,
    () -> System.out.println("Task 2"),
    integer -> integer != 5,
    () -> System.out.println("Task 3")
);
CompletableFuture[] runnables = m.entrySet().stream()
    .filter(e -> e.getKey().test(5))
    .map(Map.Entry::getValue)
    .map(CompletableFuture::runAsync)
    .toArray(CompletableFuture[]::new);
CompletableFuture composed = CompletableFuture.allOf(runnables);

Here I'm creating a map Predicate -> Runnable . Then I'm filtering out all pairs that do not fulfill the predicate and wrapping runnables into CompletableFuture s. At the end composed is a CompletableFuture , that is composed of all the desired runnables.

CompletableFuture.thenCompose() should do the trick.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;

class Scratch {

  public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 5);
    CompletableFuture<Integer> composed = future.thenCompose(number -> {
      if (number == 5)
        return simpleFuture(number);
      else
        return complexFuture(number);
    });
    System.out.println(composed.get());
  }

  private static CompletionStage<Integer> complexFuture(Integer number) {
    return CompletableFuture.completedFuture(number)
        .thenApply(i -> {
          System.out.println("I am expensive");
          return i;
        });
  }

  private static CompletableFuture<Integer> simpleFuture(Integer number) {
    return CompletableFuture.completedFuture(number)
        .thenApply(i -> {
          System.out.println("I am cheap");
          return i;
        });
  }
}

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