Close

Java CompletableFuture - Selecting Either Of the Two Completion Stages to execute a new Completion Stage

[Last Updated: Dec 7, 2018]

In previous tutorials we saw, one stage's execution may be triggered by completion of a single stage (composing), or both of two stages (combining). In this tutorial we are going to see that one stage's execution is triggered by completion of either of two stages (whichever completes first).

These methods can be grouped as:

  • applyToEither....(CompletionStage other, Function fn, .....)
  • acceptEither....(CompletionStage other, Consumer action, ....)
  • runAfterEither....(CompletionStage other, Runnable action, ....)

CompletionStage.applyToEither() methods

These methods return a new CompletionStage which is created from 'this' and 'other' given stage.
The new CompletionStage is executed when either 'this' or 'other' completion stage completes normally.
Whichever stage that finishes first, produces a result, which is fed into the new stage via a function.

public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, 
                                                            Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, 
                                                            Function<? super T, U> fn)
 public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, 
                                                           Function<? super T, U> fn, Executor executor)

Example

public class ApplyToEitherExample {

  public static void main(String[] args) {
      CompletableFuture<Integer> cf =
              CompletableFuture.supplyAsync(() -> {
                  int i = ThreadLocalRandom.current().nextInt(1, 10);
                  System.out.println("value to be return from 'this' completable: " + i);
                  return i;
              });
      CompletableFuture<Double> resultantCf = cf.applyToEither(getOtherCompletable(), a -> {
          System.out.println("Selected value: " + a);
          return Math.sqrt(a);
      });
      Double d = resultantCf.join();
      System.out.println(d);
  }

  private static CompletableFuture<Integer> getOtherCompletable() {
      return CompletableFuture.supplyAsync(() -> {
          int i = ThreadLocalRandom.current().nextInt(1, 10);
          System.out.println("value to be return from 'other' completable: " + i);
          return i;
      });
  }
}
value to be return from 'other' completable: 6
value to be return from 'this' completable: 7
Selected value: 6
2.449489742783178

In above example both 'this' and 'other' stages executed simultaneously but only one result was used. If any of them is slower to compute then that one is canceled. Let's put delay in one of them to confirm that:

public class ApplyToEitherExample2 {

  public static void main(String[] args) {
      CompletableFuture<Integer> cf =
              CompletableFuture.supplyAsync(() -> {
                  ThreadSleep(100);
                  int i = ThreadLocalRandom.current().nextInt(1, 10);
                  System.out.println("value to be return from 'this' completable: " + i);
                  return i;
              });
      CompletableFuture<Double> resultantCf = cf.applyToEither(getOtherCompletable(), a -> {
          System.out.println("Selected value: " + a);
          return Math.sqrt(a);
      });
      Double d = resultantCf.join();
      System.out.println(d);
  }

  private static CompletableFuture<Integer> getOtherCompletable() {
      return CompletableFuture.supplyAsync(() -> {
          int i = ThreadLocalRandom.current().nextInt(1, 10);
          System.out.println("value to be return from 'other' completable: " + i);
          return i;
      });
  }

  private static void ThreadSleep(int millis) {
      try {
          Thread.sleep(millis);
      } catch (InterruptedException e) {
          System.err.println(e);
      }
  }
}
value to be return from 'other' completable: 7
Selected value: 7
2.6457513110645907

CompletionStage.acceptEither() methods

In these methods, instead of a function, a consumer is used to process the result.

public CompletableFuture<Void> acceptEither(
        CompletionStage<? extends T> other, Consumer<? super T> action)
 public CompletableFuture<Void> acceptEitherAsync(
        CompletionStage<? extends T> other, Consumer<? super T> action) 
public CompletableFuture<Void> acceptEitherAsync(
        CompletionStage<? extends T> other, Consumer<? super T> action,
        Executor executor)

Example

public class AcceptEitherExample {

  public static void main(String[] args) {
      CompletableFuture<Integer> cf =
              CompletableFuture.supplyAsync(() -> {
                  int i = ThreadLocalRandom.current().nextInt(1, 10);
                  System.out.println("value to be return from 'this' completable: " + i);
                  return i;
              });
      CompletableFuture<Void> resultantCf = cf.acceptEither(getOtherCompletable(), a -> {
          System.out.println("Selected value: " + a);
          System.out.println(Math.sqrt(a));
      });
      resultantCf.join();

  }

  private static CompletableFuture<Integer> getOtherCompletable() {
      return CompletableFuture.supplyAsync(() -> {
          ThreadSleep(100);
          int i = ThreadLocalRandom.current().nextInt(1, 10);
          System.out.println("value to be return from 'other' completable: " + i);
          return i;
      });
  }

  private static void ThreadSleep(int millis) {
      try {
          Thread.sleep(millis);
      } catch (InterruptedException e) {
          System.err.println(e);
      }
  }
}
value to be return from 'this' completable: 3
Selected value: 3
1.7320508075688772

CompletionStage.runAfterEither() methods

These methods return a CompletionStage which upon execution, just runs a Runnable.

public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,
                                                  Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                       Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                       Runnable action,
                                                       Executor executor)

Example

public class RunAfterEitherExample {

  public static void main(String[] args) {
      CompletableFuture<Void> cf =
              CompletableFuture.runAsync(() -> {
                  System.out.println("Running 'this' completable");
              });
      CompletableFuture<Void> resultantCf = cf.runAfterEither(getOtherCompletable(), () -> {
          System.out.println("Running after either of the two");
      });
      resultantCf.join();
  }

  private static CompletableFuture<Void> getOtherCompletable() {
      return CompletableFuture.runAsync(() -> {
          ThreadSleep(15);
          System.out.println("Running other completable");
      });
  }

  private static void ThreadSleep(int millis) {
      try {
          Thread.sleep(millis);
      } catch (InterruptedException e) {
          System.err.println(e);
      }
  }
}
Running 'this' completable
Running after either of the two

Example Project

Dependencies and Technologies Used:

  • JDK 11
  • Maven 3.5.4

CompletionStage either method examples Select All Download
  • java-completable-future-taking-either
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • ApplyToEitherExample.java

    See Also