1. Introduction

Java 9 comes with some changes to the CompletableFuture class. Such changes were introduced as part of JEP 266 in order to address common complaints and suggestions since its introduction in JDK 8, more specifically, support for delays and timeouts, better support for subclassing and a few utility methods.

Code-wise, the API comes with eight new methods and five new static methods. To enable such additions, approximately, 1500 out of 2400 lines of code were changed (as per Open JDK).

2. Instance API Additions

As mentioned, the instance API comes with eight new additions, they are:

  1. Executor defaultExecutor()
  2. CompletableFuture newIncompleteFuture()
  3. CompletableFuture copy()
  4. CompletionStage minimalCompletionStage()
  5. CompletableFuture completeAsync(Supplier<? extends T> supplier, Executor executor)
  6. CompletableFuture completeAsync(Supplier<? extends T> supplier)
  7. CompletableFuture orTimeout(long timeout, TimeUnit unit)
  8. CompletableFuture completeOnTimeout(T value, long timeout, TimeUnit unit)

2.1. Method defaultExecutor()

Signature: Executor defaultExecutor()

Returns the default Executor used for async methods that do not specify an Executor.

new CompletableFuture().defaultExecutor()

This can be overridden by subclasses returning an executor providing, at least, one independent thread.

2.2. Method newIncompleteFuture()

Signature: CompletableFuture newIncompleteFuture()

The newIncompleteFuture, also known as the “virtual constructor”, is used to get a new completable future instance of the same type.

new CompletableFuture().newIncompleteFuture()

This method is especially useful when subclassing CompletableFuture, mainly because it is used internally in almost all methods returning a new CompletionStage, allowing subclasses to control what subtype gets returned by such methods.

2.3. Method copy()

Signature: CompletableFuture copy()

This method returns a new CompletableFuture which:

  • When this gets completed normally, the new one gets completed normally also
  • When this gets completed exceptionally with exception X, the new one is also completed exceptionally with a CompletionException with X as cause
new CompletableFuture().copy()

This method may be useful as a form of “defensive copying”, to prevent clients from completing, while still being able to arrange dependent actions on a specific instance of CompletableFuture.

2.4. Method minimalCompletionStage()

Signature: CompletionStage minimalCompletionStage()

This method returns a new CompletionStage which behaves in the exact same way as described by the copy method, however, such new instance throws UnsupportedOperationException in every attempt to retrieve or set the resolved value.

new CompletableFuture().minimalCompletionStage()

A new CompletableFuture with all methods available can be retrieved by using the toCompletableFuture method available on the CompletionStage API.

2.5. Methods completeAsync()

The completeAsync method should be used to complete the CompletableFuture asynchronously using the value given by the Supplier provided.

Signatures:

CompletableFuture<T> completeAsync(Supplier<? extends T> supplier, Executor executor)
CompletableFuture<T> completeAsync(Supplier<? extends T> supplier)

The difference between this two overloaded methods is the existence of the second argument, where the Executor running the task can be specified. If none is provided, the default executor (returned by the defaultExecutor method) will be used.

2.6. Methods orTimeout()

Signature: CompletableFuture orTimeout(long timeout, TimeUnit unit)

new CompletableFuture().orTimeout(1, TimeUnit.SECONDS)

Resolves the CompletableFuture exceptionally with TimeoutException, unless it is completed before the specified timeout.

2.7. Method completeOnTimeout()

Signature: CompletableFuture completeOnTimeout(T value, long timeout, TimeUnit unit)

new CompletableFuture().completeOnTimeout(value, 1, TimeUnit.SECONDS)

Completes the CompletableFuture normally with the specified value unless it is completed before the specified timeout.

3. Static API Additions

Some utility methods were also added. They are:

  1. Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)
  2. Executor delayedExecutor(long delay, TimeUnit unit)
  3. CompletionStage completedStage(U value)
  4. CompletionStage failedStage(Throwable ex)
  5. CompletableFuture failedFuture(Throwable ex)

3.1. Methods delayedExecutor

Signatures:

Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)
Executor delayedExecutor(long delay, TimeUnit unit)

Returns a new Executor that submits a task to the given base executor after the given delay (or no delay if non-positive). Each delay commences upon invocation of the returned executor’s execute method. If no executor is specified the default executor (ForkJoinPool.commonPool()) will be used.

3.2. Methods completedStage and failedStage

Signatures:

<U> CompletionStage<U> completedStage(U value)
<U> CompletionStage<U> failedStage(Throwable ex)

This utility methods return already resolved CompletionStage instances, either completed normally with a value (completedStage) or completed exceptionally (failedStage) with the given exception.

3.3. Method failedFuture

Signature: CompletableFuture failedFuture(Throwable ex)

The failedFuture method adds the ability to specify an already completed exceptionally CompleatebleFuture instance.

4. Example Use Cases

Within this section, one will show some examples on how to use some of the new API.

4.1. Delay

This example will show how to delay the completion of a CompletableFuture with a specific value by one second. That can be achieved by using the completeAsync method together with the delayedExecutor.

CompletableFuture<Object> future = new CompletableFuture<>();
future.completeAsync(() -> input, CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS));

4.2. Complete With Value on Timeout

Another way to achieve a delayed result is to use the completeOnTimeout method. This example defines a CompletableFuture that will be resolved with a given input if it stays unresolved after 1 second.

CompletableFuture<Object> future = new CompletableFuture<>();
future.completeOnTimeout(input, 1, TimeUnit.SECONDS);

4.3. Timeout

Another possibility is timing out which resolves the future exceptionally with TimeoutException. For example, having the CompletableFuture timing out after 1 second given it is not completed before that.

CompletableFuture<Object> future = new CompletableFuture<>();
future.orTimeout(1, TimeUnit.SECONDS);

5. Conclusion

In conclusion, Java 9 comes with several additions to the CompletableFuture API, it now has better support for subclassing, thanks to the newIncompleteFuture virtual constructor, it is possible to take control over the CompletionStage instances returned in most of the CompletionStage API.

It has, definitely, better support for delays and timeouts as shown previously. The utility methods added follow a sensible pattern, giving CompletableFuture a convenient way to specify resolved instances.

The examples used in this article can be found in our GitHub repository.