completable-futures
completable-futures is a set of utility functions to simplify working with asynchronous code in Java8.
Usage
Using completable-futures
requires Java 8 but has no additional dependencies. It is meant to be
included as a library in other software. To import it with maven, add this to your pom:
<dependency>
<groupId>com.spotify</groupId>
<artifactId>completable-futures</artifactId>
<version>0.3.0</version>
</dependency>
Features
Combining more than two things
The builtin CompletableFuture API includes future.thenCombine(otherFuture, function)
but if you
want to combine more than two things it gets trickier. The CompletableFutures
class contains the
following APIs to simplify this use-case:
allAsList
If you want to join a list of futures of uniform type, use allAsList
. This returns a future which
completes to a list of all values of its inputs:
List<CompletableFuture<String>> futures = asList(completedFuture("a"), completedFuture("b"));
CompletableFuture<List<String>> joined = CompletableFutures.allAsList(futures);
successfulAsList
Works like allAsList
, but futures that fail will not fail the joined future. Instead, the
defaultValueMapper function will be called once for each failed future and value returned will be
put in the resulting list on the place corresponding to the failed future. The default value
returned by the function may be anything, such as null
or Optional.empty()
.
List<CompletableFuture<String>> input = asList(
completedFuture("a"),
exceptionallyCompletedFuture(new RuntimeException("boom")));
CompletableFuture<List<String>> joined = CompletableFutures.successfulAsList(input, t -> "default");
joinList
joinList
is a stream collector that combines multiple futures into a list. This is handy if you
apply an asynchronous operation to a collection of entities:
collection.stream()
.map(this::someAsyncFunction)
.collect(CompletableFutures.joinList())
.thenApply(this::consumeList)
combine
If you want to combine more than two futures of different types, use the combine
method:
CompletableFutures.combine(f1, f2, (a, b) -> a + b);
CompletableFutures.combine(f1, f2, f3, (a, b, c) -> a + b + c);
CompletableFutures.combine(f1, f2, f3, f4, (a, b, c, d) -> a + b + c + d);
CompletableFutures.combine(f1, f2, f3, f4, f5, (a, b, c, d, e) -> a + b + c + d + e);
Scheduling
Polling an external resource
If you are dealing with a long-running external task that only exposes a polling API, you can transform that into a future like so:
Supplier<Optional<T>> pollingTask = () -> Optional.ofNullable(resource.result());
Duration frequency = Duration.ofSeconds(2);
CompletableFuture<T> result = CompletableFutures.poll(pollingTask, frequency, executor);
Missing parts of the CompletableFuture API
The CompletableFutures
class includes utility functions for operating on futures that is missing
from the builtin API.
handleCompose
Like CompletableFuture.handle
but lets you return a new CompletionStage
instead of a
direct value.
CompletionStage<String> composed = handleCompose(future, (value, throwable) -> completedFuture("hello"));
exceptionallyCompose
Like CompletableFuture.exceptionally
but lets you return a new CompletionStage
instead of a
direct value.
CompletionStage<String> composed = CompletableFutures.exceptionallyCompose(future, throwable -> completedFuture("fallback"));
dereference
Unwrap a CompletionStage<CompletionStage<T>>
to a plain CompletionStage<T>
.
CompletionStage<CompletionStage<String>> wrapped = completedFuture(completedFuture("hello"));
CompletionStage<String> unwrapped = CompletableFutures.dereference(wrapped);
exceptionallyCompletedFuture
Creates a new future that is already exceptionally completed with the given exception.
return CompletableFutures.exceptionallyCompletedFuture(new RuntimeException("boom"));
License
Copyright 2016 Spotify AB. Licensed under the Apache License, Version 2.0.
Code of Conduct
This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.