As featured in ExecutorService:
ExecutorService threadPool = Executors.newThreadPool();
Future<Result> future = threadPool.submit(() => calculate(arg));
Result result = future.get(); // throws InterruptedException, ExecutionException
// or..
Result result = future.get(1, TimeUnit.MINUTES); // or TimeoutException
A Future is only good for one thing: blocking on it until it completes.
Dealing with multiple Futures for anything other than "wait for all of them to complete" gets harder and harder.
How do you "wait for the first one to be complete"? (apart from specialised constructs like CompletionService)
Both Ning and Guava define a ListenableFuture
which calls a callback
when the task is complete.
Guava has a Futures
utility class for combining them together, e.g.
transform
, whenAllSucceed
scala.Future is quite different to JDK5 Future, and provides methods for transforming/composing (map, flatMap etc)
JavaScript (outside node.js itself) has largely moved to using Promises to encapsulate asyncronous completion:
new Promise((resolve, reject) => ajax({
success: data => resolve(data),
error: (xhr, status, e) => reject(e) }))
.then(data => setState({ data, error: null }))
.catch(error => setState({ error, data: null }));
All these other libraries provide a way of building a pipeline (actually, a graph) of (possibly) asynchronous actions, which can bear either a positive result value or an exception value.
With JDK8, time for Java to join the party.
CompletableFuture actually implements Future, so could be used the same way. Nevertheless:
CompletableFuture<Result> future =
CompletableFuture.supplyAsync(() => calculate(arg), executor);
Result result = future.join();
// JDK9 only
Result result = future.orTimeout(1, TimeUnit.MINUTES).join();
They also work like promises/Scala futures, implementing the CompletionStage
with a plethora of transforming methods:
supplyAsync(this::calculate) // or runAsync(Runnable)
.thenApply(this::mangleResult) // like scala.Future#map, or Promise.then
.thenApplyAsync(this::mangleResult, executor) // transform run on a different thread
.thenCompose(this::nextStage) // like scala.Future#flatMap, or Promise.then
.thenCombine(otherFuture, (v1, v2) -> x)) // join two futures ("and")
.applyToEither(otherFuture, (v1, v2) -> x)) // join two futures ("or")
.exceptionally(exception -> x) // like Promise.catch
.handle((value, exception) -> x) // better
Practically all have three variants: synchronous, asynchronous using default fork-join pool, asynchronous using specified Executor.
Say goodbye to ExecutorService
too - these only need the Executor
SAM interface
Also a smattering of utility methods:
CompletableFuture<?> allOf = allOf(future1, future2, ...); // or anyOf
CompletableFuture<Result> completed = completedFuture(value);
CompletableFuture<?> failed = failedFuture(value); // JDK9
They also work like Guava SettableFuture
or Scala Promise
objects:
CompletableFuture<Result> future = new CompletableFuture<>();
future.complete(value);
future.completeExceptionally(new Exception());
future.completeAsync(() => this::calculate); // JDK9
Examples