Close

Java CompletableFuture - Combining Completion Stages

[Updated: Dec 7, 2018, Created: Oct 19, 2018]

In this tutorial we will go through those methods of CompletionStage which take another CompletionStage instance as a parameter and combine the both stages' results via BiFunction or BiConsumer or Runnable.

These methods can be categorized as:

  • thenCombine....(CompletionStage other, BiFunction fn, ....)
  • thenAcceptBoth....(CompletionStage other, BiConsumer action, ....)
  • runAfterBoth...(CompletionStage other, Runnable action, ....)

CompletionStage.thenCombine() methods

These methods combine this CompletionStage<T>'s result T with another CompletionStage's result U using a BiFunction. The BiFunction returns a resultant computed value of type V.

public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,  
                                              BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,  
                                                   BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
                                                   BiFunction<? super T,? super U,? extends V> fn, 
                                                   Executor executor)

Example

This example uses int[] (from other CompletionStage), finds their values raised to the power of another number (from this CompletionStage) and returns the sum:

package com.logicbig.example;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;

public class ThenCombineExample {
  public static void main(String[] args) {
      CompletableFuture<Integer> cf = CompletableFuture
              .supplyAsync(() -> {
                  int x = ThreadLocalRandom.current().nextInt(1, 5);
                  System.out.println("Main stage: " + x);
                  return x;
              });

      CompletableFuture<Double> finalCf = cf.thenCombine(
              getOther(),
              (x, ints) -> Arrays.stream(ints).mapToDouble(i -> Math.pow(i, x))
                                 .sum());
      Double d = finalCf.join();
      System.out.println(d);
  }

  private static CompletableFuture<int[]> getOther() {
      CompletableFuture<int[]> otherCf = CompletableFuture
              .supplyAsync(() -> {
                  int[] ints = IntStream.range(1, 5)
                                        .map(i -> ThreadLocalRandom.current().nextInt(5, 10))
                                        .toArray();
                  System.out.println("Other stage: " + Arrays.toString(ints));
                  return ints;
              });
      return otherCf;
  }
}
Main stage: 2
Other stage: [5, 7, 6, 7]
159.0

CompletionStage.thenAcceptBoth() methods

These methods instead of using a BiFunction, use BiConsumer. That means the results from both CompletionStages are consumed and the resultant CompletionStage returns void.

public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
                                                 BiConsumer<? super T, ? super U> action) 
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
                                                      BiConsumer<? super T, ? super U> action) 
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
                                                      BiConsumer<? super T, ? super U> action,
                                                      Executor executor) 

Example

package com.logicbig.example;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;

public class ThenAcceptBothExample {
  public static void main(String[] args) {
      CompletableFuture<Integer> cf = CompletableFuture
              .supplyAsync(() -> {
                  int x = ThreadLocalRandom.current().nextInt(1, 5);
                  System.out.println("Main stage: " + x);
                  return x;
              });

      CompletableFuture<Void> finalCf = cf.thenAcceptBoth(
              getOther(),
              (x, ints) -> {
                  double sum = Arrays.stream(ints).mapToDouble(n -> Math.pow(n, x))
                                     .sum();
                  System.out.println(sum);
              });
      finalCf.join();
  }

  private static CompletableFuture<int[]> getOther() {
      CompletableFuture<int[]> otherCf = CompletableFuture
              .supplyAsync(() -> {
                  int[] ints = IntStream.range(1, 5)
                                        .map(i -> ThreadLocalRandom.current().nextInt(5, 10))
                                        .toArray();
                  System.out.println("Other stage: " + Arrays.toString(ints));
                  return ints;
              });
      return otherCf;
  }
}
Main stage: 4
Other stage: [9, 7, 8, 6]
14354.0

CompletionStage.runAfterBoth() methods

These methods simply use a Runnable (instead of a BiFunction or BiConsumer) which is executed after both stages complete normally.

  public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,
                                              Runnable action)
  public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
                                                   Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
                                                 Runnable action,
                                                 Executor executor)

Example

package com.logicbig.example;

import java.util.concurrent.CompletableFuture;

public class RunAfterBothExample {
  public static void main(String[] args) {
      CompletableFuture<Void> cf = CompletableFuture.runAsync(
              () -> System.out.println("In first stage"));
      CompletableFuture<Void> otherCf = CompletableFuture.runAsync(
              () -> System.out.println("In second stage"));
      cf.runAfterBoth(otherCf, () -> System.out.println("after both stages"));
      cf.join();
  }
}
In second stage
In first stage
after both stages

Example Project

Dependencies and Technologies Used:

  • JDK 11
  • Maven 3.5.4

CompletableFuture - Combining Two Completion Stages Select All Download
  • java-completable-future-task-combining
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • ThenCombineExample.java

    See Also