Close

Java CompletableFuture - Understanding CompletionStage.whenComplete() method

[Updated: Feb 9, 2019, Created: Feb 9, 2019]

In the last tutorial we saw how to handle exceptions with CompletableFuture.

Following are the another groups of methods in CompletionStage() class which are similar to CompletionStage.handle(....) methods but are not really for exception handling.

public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)

handle() vs whenComplete()

The above methods, accept BiConsumer, whereas CompletionStage.handle(....) methods accept BiFunction. That means handle() methods are allowed to return a result (in case of exception a recovering result) thus they can handle the exception. On the other hand, whenComplete() methods cannot return a results. So they are used as merely callbacks that do not interfere in the processing pipeline of CompletionStages.

If there's an unhandled exception coming from the stages before 'whenComplete' stage then that exception is passed through as it is. In other words if the upstream CompletionStage completes exceptionally, the CompletionStage returned by whenComplete() also completes exceptionally.

Examples

package com.logicbig.example;

import java.util.concurrent.CompletableFuture;

public class Example1WhenComplete {
  public static void main(String[] args) throws Exception {
      runTasks(2);
      runTasks(0);
  }

  private static void runTasks(int i) {
      System.out.printf("-- input: %s --%n", i);
      CompletableFuture
              .supplyAsync(() -> {
                  return 16 / i;
              })
              .whenComplete((input, exception) -> {
                  if (exception != null) {
                      System.out.println("exception occurs");
                      System.err.println(exception);
                  } else {
                      System.out.println("no exception, got result: " + input);
                  }
              })
              .thenApply(input -> input * 3)
              .thenAccept(System.out::println);

  }
}
-- input: 2 --
no exception, got result: 8
24
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero

Following example handles exception in an exceptionally() stage placed after whenComplete() stage:

package com.logicbig.example;

import java.util.concurrent.CompletableFuture;

public class Example2WhenComplete {
  public static void main(String[] args) throws Exception {
      runTasks(2);
      runTasks(0);
  }

  private static void runTasks(int i) {
      System.out.printf("-- input: %s --%n", i);
      CompletableFuture
              .supplyAsync(() -> {
                  return 16 / i;
              })
              .whenComplete((input, exception) -> {
                  if (exception != null) {
                      System.out.println("exception occurs");
                      System.err.println(exception);
                  } else {
                      System.out.println("no exception, got result: " + input);
                  }
              })
              .exceptionally(throwable ->
              {
                  System.out.println("recovering in exceptionally: " + throwable);
                  return 1;
              })
              .thenApply(input -> input * 3)
              .thenAccept(System.out::println);

  }
}
-- input: 2 --
no exception, got result: 8
24
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
recovering in exceptionally: java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
3

Following example handles exception in an exceptionally() stage placed before whenComplete() stage:

package com.logicbig.example;

import java.util.concurrent.CompletableFuture;

public class Example3WhenComplete {
  public static void main(String[] args) throws Exception {
      runTasks(2);
      runTasks(0);
  }

  private static void runTasks(int i) {
      System.out.printf("-- input: %s --%n", i);
      CompletableFuture
              .supplyAsync(() -> {
                  return 16 / i;
              })
              .exceptionally(throwable -> {
                  System.out.println("recovering in exceptionally: " + throwable);
                  return 1;
              })
              .whenComplete((input, exception) -> {

                  if (exception != null) {
                      System.out.println("exception occurs");
                      System.err.println(exception);
                  } else {
                      System.out.println("no exception, got result: " + input);
                  }
              })
              .thenApply(input -> input * 3)
              .thenAccept(System.out::println);

  }
}
-- input: 2 --
no exception, got result: 8
24
-- input: 0 --
recovering in exceptionally: java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
no exception, got result: 1
3

Example Project

Dependencies and Technologies Used:

  • JDK 11
  • Maven 3.5.4

CompletionStage.whenComplete() Example\ Select All Download
  • java-completable-future-when-complete
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • Example1WhenComplete.java

    See Also