Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
javaslang.concurrent.Future Maven / Gradle / Ivy
/* / \____ _ _ ____ ______ / \ ____ __ _______
* / / \/ \ / \/ \ / /\__\/ // \/ \ // /\__\ JΛVΛSLΛNG
* _/ / /\ \ \/ / /\ \\__\\ \ // /\ \ /\\/ \ /__\ \ Copyright 2014-2016 Javaslang, http://javaslang.io
* /___/\_/ \_/\____/\_/ \_/\__\/__/\__\_/ \_// \__/\_____/ Licensed under the Apache License, Version 2.0
*/
package javaslang.concurrent;
import javaslang.API;
import javaslang.Tuple;
import javaslang.Tuple2;
import javaslang.Value;
import javaslang.collection.Iterator;
import javaslang.collection.List;
import javaslang.collection.Seq;
import javaslang.collection.Stream;
import javaslang.control.Option;
import javaslang.control.Try;
import javaslang.control.Try.CheckedFunction;
import javaslang.control.Try.CheckedPredicate;
import javaslang.control.Try.CheckedRunnable;
import javaslang.control.Try.CheckedSupplier;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*;
/**
* A Future is a computation result that becomes available at some point. All operations provided are non-blocking.
*
* The underlying {@code ExecutorService} is used to execute asynchronous handlers, e.g. via
* {@code onComplete(...)}.
*
* A Future has two states: pending and completed.
*
* Pending: The computation is ongoing. Only a pending future may be completed or cancelled.
* Completed: The computation finished successfully with a result, failed with an exception or was cancelled.
*
* Callbacks may be registered on a Future at each point of time. These actions are performed as soon as the Future
* is completed. An action which is registered on a completed Future is immediately performed. The action may run on
* a separate Thread, depending on the underlying ExecutorService. Actions which are registered on a cancelled
* Future are performed with the failed result.
*
* @param Type of the computation result.
* @author Daniel Dietrich
* @since 2.0.0
*/
public interface Future extends Value {
/**
* The default executor service is {@link Executors#newCachedThreadPool()}.
* Please note that it may prevent the VM from shutdown.}
*/
ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
/**
* Creates a failed {@code Future} with the given {@code exception}, backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param exception The reason why it failed.
* @param The value type of a successful result.
* @return A failed {@code Future}.
* @throws NullPointerException if exception is null
*/
static Future failed(Throwable exception) {
Objects.requireNonNull(exception, "exception is null");
return failed(DEFAULT_EXECUTOR_SERVICE, exception);
}
/**
* Creates a failed {@code Future} with the given {@code exception}, backed by the given {@link ExecutorService}.
*
* @param executorService An executor service.
* @param exception The reason why it failed.
* @param The value type of a successful result.
* @return A failed {@code Future}.
* @throws NullPointerException if executorService or exception is null
*/
static Future failed(ExecutorService executorService, Throwable exception) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(exception, "exception is null");
return Promise. failed(executorService, exception).future();
}
/**
* Returns a {@code Future} that eventually succeeds with the first result of the given {@code Future}s which
* matches the given {@code predicate}. If no result matches, the {@code Future} will contain {@link Option.None}.
*
* The returned {@code Future} is backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param futures An iterable of futures.
* @param predicate A predicate that tests successful future results.
* @param Result type of the futures.
* @return A Future of an {@link Option} of the first result of the given {@code futures} that satisfies the given {@code predicate}.
* @throws NullPointerException if one of the arguments is null
*/
static Future> find(Iterable extends Future extends T>> futures, Predicate super T> predicate) {
return find(DEFAULT_EXECUTOR_SERVICE, futures, predicate);
}
/**
* Returns a {@code Future} that eventually succeeds with the first result of the given {@code Future}s which
* matches the given {@code predicate}. If no result matches, the {@code Future} will contain {@link Option.None}.
*
* The returned {@code Future} is backed by the given {@link ExecutorService}.
*
* @param executorService An executor service.
* @param futures An iterable of futures.
* @param predicate A predicate that tests successful future results.
* @param Result type of the futures.
* @return A Future of an {@link Option} of the first result of the given {@code futures} that satisfies the given {@code predicate}.
* @throws NullPointerException if one of the arguments is null
*/
static Future> find(ExecutorService executorService, Iterable extends Future extends T>> futures, Predicate super T> predicate) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(futures, "futures is null");
Objects.requireNonNull(predicate, "predicate is null");
final Promise > promise = Promise.make(executorService);
final List> list = List.ofAll(futures);
if (list.isEmpty()) {
promise.success(Option.none());
} else {
final AtomicInteger count = new AtomicInteger(list.length());
list.forEach(future -> future.onComplete(result -> {
synchronized (count) {
// if the promise is already completed we already found our result and there is nothing more to do.
if (!promise.isCompleted()) {
// when there are no more results we return a None
final boolean wasLast = count.decrementAndGet() == 0;
// when result is a Failure or predicate is false then we check in onFailure for finish
result.filter(predicate)
.onSuccess(value -> promise.trySuccess(Option.some(value)))
.onFailure(ignored -> {
if (wasLast) {
promise.trySuccess(Option.none());
}
});
}
}
}));
}
return promise.future();
}
/**
* Returns a new {@code Future} that will contain the result of the first of the given futures that is completed,
* backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param futures An iterable of futures.
* @param The result type.
* @return A new {@code Future}.
* @throws NullPointerException if futures is null
*/
static Future firstCompletedOf(Iterable extends Future extends T>> futures) {
return firstCompletedOf(DEFAULT_EXECUTOR_SERVICE, futures);
}
/**
* Returns a new {@code Future} that will contain the result of the first of the given futures that is completed,
* backed by the given {@link ExecutorService}.
*
* @param executorService An executor service.
* @param futures An iterable of futures.
* @param The result type.
* @return A new {@code Future}.
* @throws NullPointerException if executorService or futures is null
*/
static Future firstCompletedOf(ExecutorService executorService, Iterable extends Future extends T>> futures) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(futures, "futures is null");
final Promise promise = Promise.make(executorService);
final Consumer> completeFirst = promise::tryComplete;
futures.forEach(future -> future.onComplete(completeFirst));
return promise.future();
}
/**
* Returns a Future which contains the result of the fold of the given future values. If any future or the fold
* fail, the result is a failure.
*
* The resulting {@code Future} is backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param futures An iterable of futures.
* @param zero The zero element of the fold.
* @param f The fold operation.
* @param The result type of the given {@code Futures}.
* @param The fold result type.
* @return A new {@code Future} that will contain the fold result.
* @throws NullPointerException if futures or f is null.
*/
static Future fold(Iterable extends Future extends T>> futures, U zero, BiFunction super U, ? super T, ? extends U> f) {
return fold(DEFAULT_EXECUTOR_SERVICE, futures, zero, f);
}
/**
* Returns a Future which contains the result of the fold of the given future values. If any future or the fold
* fail, the result is a failure.
*
* The resulting {@code Future} is backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService}.
* @param futures An iterable of futures.
* @param zero The zero element of the fold.
* @param f The fold operation.
* @param The result type of the given {@code Futures}.
* @param The fold result type.
* @return A new {@code Future} that will contain the fold result.
* @throws NullPointerException if executorService, futures or f is null.
*/
static Future fold(ExecutorService executorService, Iterable extends Future extends T>> futures, U zero, BiFunction super U, ? super T, ? extends U> f) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(futures, "futures is null");
Objects.requireNonNull(f, "f is null");
if (!futures.iterator().hasNext()) {
return successful(executorService, zero);
} else {
return sequence(executorService, futures).map(seq -> seq.foldLeft(zero, f));
}
}
/**
* Creates a {@code Future} with the given java.util.concurrent.Future, backed by the {@link #DEFAULT_EXECUTOR_SERVICE}
*
* @param future A {@link java.util.concurrent.Future}
* @param Result type of the Future
* @return A new {@code Future} wrapping the result of the Java future
* @throws NullPointerException if future is null
*/
static Future fromJavaFuture(java.util.concurrent.Future future) {
Objects.requireNonNull(future, "future is null");
return Future.of(DEFAULT_EXECUTOR_SERVICE, future::get);
}
/**
* Creates a {@code Future} with the given java.util.concurrent.Future, backed by given {@link ExecutorService}
*
* @param executorService An {@link ExecutorService}
* @param future A {@link java.util.concurrent.Future}
* @param Result type of the Future
* @return A new {@code Future} wrapping the result of the Java future
* @throws NullPointerException if executorService or future is null
*/
static Future fromJavaFuture(ExecutorService executorService, java.util.concurrent.Future future) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(future, "future is null");
return Future.of(executorService, future::get);
}
/**
* Creates a {@code Future} from a {@link Try}, backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param result The result.
* @param The value type of a successful result.
* @return A completed {@code Future} which contains either a {@code Success} or a {@code Failure}.
* @throws NullPointerException if result is null
*/
static Future fromTry(Try extends T> result) {
return fromTry(DEFAULT_EXECUTOR_SERVICE, result);
}
/**
* Creates a {@code Future} from a {@link Try}, backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService}.
* @param result The result.
* @param The value type of a successful result.
* @return A completed {@code Future} which contains either a {@code Success} or a {@code Failure}.
* @throws NullPointerException if executorService or result is null
*/
static Future fromTry(ExecutorService executorService, Try extends T> result) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(result, "result is null");
return Promise. fromTry(executorService, result).future();
}
/**
* Narrows a widened {@code Future extends T>} to {@code Future}
* by performing a type safe-cast. This is eligible because immutable/read-only
* collections are covariant.
*
* @param future A {@code Future}.
* @param Component type of the {@code Future}.
* @return the given {@code future} instance as narrowed type {@code Future}.
*/
@SuppressWarnings("unchecked")
static Future narrow(Future extends T> future) {
return (Future) future;
}
/**
* Starts an asynchronous computation, backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param computation A computation.
* @param Type of the computation result.
* @return A new Future instance.
* @throws NullPointerException if computation is null.
*/
static Future of(CheckedSupplier extends T> computation) {
return Future.of(DEFAULT_EXECUTOR_SERVICE, computation);
}
/**
* Starts an asynchronous computation, backed by the given {@link ExecutorService}.
*
* @param executorService An executor service.
* @param computation A computation.
* @param Type of the computation result.
* @return A new Future instance.
* @throws NullPointerException if one of executorService of computation is null.
*/
static Future of(ExecutorService executorService, CheckedSupplier extends T> computation) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(computation, "computation is null");
final FutureImpl future = new FutureImpl<>(executorService);
future.run(computation);
return future;
}
/**
* Returns a Future which contains the reduce result of the given future values. The zero is the result of the
* first future that completes. If any future or the reduce operation fail, the result is a failure.
*
* The resulting {@code Future} is backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param futures An iterable of futures.
* @param f The reduce operation.
* @param The result type of the given {@code Futures}.
* @return A new {@code Future} that will contain the reduce result.
* @throws NullPointerException if executorService, futures or f is null.
*/
static Future reduce(Iterable extends Future extends T>> futures, BiFunction super T, ? super T, ? extends T> f) {
return reduce(DEFAULT_EXECUTOR_SERVICE, futures, f);
}
/**
* Returns a Future which contains the reduce result of the given future values. The zero is the result of the
* first future that completes. If any future or the reduce operation fail, the result is a failure.
*
* The resulting {@code Future} is backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService}.
* @param futures An iterable of futures.
* @param f The reduce operation.
* @param The result type of the given {@code Futures}.
* @return A new {@code Future} that will contain the reduce result.
* @throws NullPointerException if executorService, futures or f is null.
*/
static Future reduce(ExecutorService executorService, Iterable extends Future extends T>> futures, BiFunction super T, ? super T, ? extends T> f) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(futures, "futures is null");
Objects.requireNonNull(f, "f is null");
if (!futures.iterator().hasNext()) {
throw new NoSuchElementException("Future.reduce on empty futures");
} else {
return Future. sequence(futures).map(seq -> seq.reduceLeft(f));
}
}
/**
* Runs an asynchronous computation, backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param unit A unit of work.
* @return A new Future instance which results in nothing.
* @throws NullPointerException if unit is null.
*/
static Future run(CheckedRunnable unit) {
return run(DEFAULT_EXECUTOR_SERVICE, unit);
}
/**
* Starts an asynchronous computation, backed by the given {@link ExecutorService}.
*
* @param executorService An executor service.
* @param unit A unit of work.
* @return A new Future instance which results in nothing.
* @throws NullPointerException if one of executorService of unit is null.
*/
static Future run(ExecutorService executorService, CheckedRunnable unit) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(unit, "unit is null");
return Future.of(executorService, () -> {
unit.run();
return null;
});
}
/**
* Reduces many {@code Future}s into a single {@code Future} by transforming an
* {@code Iterable>} into a {@code Future>}.
*
* The resulting {@code Future} is backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
*
*
* If all of the given Futures succeed, sequence() succeeds too:
* // = Future(Success(Seq(1, 2)))
* sequence(
* List.of(
* Future.of(() -> 1),
* Future.of(() -> 2)
* )
* );
*
*
* If a given Future fails, sequence() fails too:
* // = Future(Failure(Error)))
* sequence(
* List.of(
* Future.of(() -> 1),
* Future.of(() -> { throw new Error(); }
* )
* );
*
*
*
* @param futures An {@code Iterable} of {@code Future}s.
* @param Result type of the futures.
* @return A {@code Future} of a {@link Seq} of results.
* @throws NullPointerException if futures is null.
*/
static Future> sequence(Iterable extends Future extends T>> futures) {
return sequence(DEFAULT_EXECUTOR_SERVICE, futures);
}
/**
* Reduces many {@code Future}s into a single {@code Future} by transforming an
* {@code Iterable>} into a {@code Future>}.
*
* The resulting {@code Future} is backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService}.
* @param futures An {@code Iterable} of {@code Future}s.
* @param Result type of the futures.
* @return A {@code Future} of a {@link Seq} of results.
* @throws NullPointerException if executorService or futures is null.
*/
static Future> sequence(ExecutorService executorService, Iterable extends Future extends T>> futures) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(futures, "futures is null");
final Future> zero = successful(executorService, Stream.empty());
final BiFunction>, Future extends T>, Future>> f =
(result, future) -> result.flatMap(seq -> future.map(seq::append));
return Iterator.ofAll(futures).foldLeft(zero, f);
}
/**
* Creates a succeeded {@code Future}, backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param result The result.
* @param The value type of a successful result.
* @return A succeeded {@code Future}.
*/
static Future successful(T result) {
return successful(DEFAULT_EXECUTOR_SERVICE, result);
}
/**
* Creates a succeeded {@code Future}, backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService}.
* @param result The result.
* @param The value type of a successful result.
* @return A succeeded {@code Future}.
* @throws NullPointerException if executorService is null
*/
static Future successful(ExecutorService executorService, T result) {
Objects.requireNonNull(executorService, "executorService is null");
return Promise.successful(executorService, result).future();
}
/**
* Maps the values of an iterable in parallel to a sequence of mapped values into a single {@code Future} by
* transforming an {@code Iterable extends T>} into a {@code Future>}.
*
* The resulting {@code Future} is backed by the {@link #DEFAULT_EXECUTOR_SERVICE}.
*
* @param values An {@code Iterable} of {@code Future}s.
* @param mapper A mapper of values to Futures
* @param The type of the given values.
* @param The mapped value type.
* @return A {@code Future} of a {@link Seq} of results.
* @throws NullPointerException if values or f is null.
*/
static Future> traverse(Iterable extends T> values, Function super T, ? extends Future extends U>> mapper) {
return traverse(DEFAULT_EXECUTOR_SERVICE, values, mapper);
}
/**
* Maps the values of an iterable in parallel to a sequence of mapped values into a single {@code Future} by
* transforming an {@code Iterable extends T>} into a {@code Future>}.
*
* The resulting {@code Future} is backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService}.
* @param values An {@code Iterable} of values.
* @param mapper A mapper of values to Futures
* @param The type of the given values.
* @param The mapped value type.
* @return A {@code Future} of a {@link Seq} of results.
* @throws NullPointerException if executorService, values or f is null.
*/
static Future> traverse(ExecutorService executorService, Iterable extends T> values, Function super T, ? extends Future extends U>> mapper) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(values, "values is null");
Objects.requireNonNull(mapper, "mapper is null");
return sequence(Iterator.ofAll(values).map(mapper));
}
// -- non-static Future API
/**
* Support for chaining of callbacks that are guaranteed to be executed in a specific order.
*
* An exception, which occurs when performing the given {@code action}, is not propagated to the outside.
* In other words, subsequent actions are performed based on the value of the original Future.
*
* Example:
*
* // prints Success(1)
* Future.of(() -> 1)
* .andThen(t -> { throw new Error(""); })
* .andThen(System.out::println);
*
*
* @param action A side-effecting action.
* @return A new Future that contains this result and which is completed after the given action was performed.
* @throws NullPointerException if action is null
*/
default Future andThen(Consumer super Try> action) {
Objects.requireNonNull(action, "action is null");
final Promise promise = Promise.make(executorService());
onComplete(t -> {
Try.run(() -> action.accept(t));
promise.complete(t);
});
return promise.future();
}
/**
* Blocks the current Thread until this Future completed or returns immediately if this Future is already completed.
*/
void await();
/**
* Cancels the Future. A running thread is interrupted.
*
* If the Future was successfully cancelled, the result is a {@code Failure(CancellationException)}.
*
* @return {@code false}, if this {@code Future} is already completed or could not be cancelled, otherwise {@code true}.
*/
default boolean cancel() {
return cancel(true);
}
/**
* Cancels the Future. A pending Future may be interrupted, depending on the underlying ExecutionService.
*
* If the Future was successfully cancelled, the result is a {@code Failure(CancellationException)}.
*
* @param mayInterruptIfRunning {@code true} if a running thread should be interrupted, otherwise a running thread
* is allowed to complete its computation.
* @return {@code false}, if this {@code Future} is already completed or could not be cancelled, otherwise {@code true}.
* @see java.util.concurrent.Future#cancel(boolean)
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* Returns the {@link ExecutorService} used by this {@code Future}.
*
* @return The underlying {@code ExecutorService}.
*/
ExecutorService executorService();
/**
* A projection that inverses the result of this Future.
*
* If this Future succeeds, the failed projection returns a failure containing a {@code NoSuchElementException}.
*
* If this Future fails, the failed projection returns a success containing the exception.
*
* @return A new Future which contains an exception at a point of time.
*/
default Future failed() {
final Promise promise = Promise.make(executorService());
onComplete(result -> {
if (result.isFailure()) {
promise.success(result.getCause());
} else {
promise.failure(new NoSuchElementException("Future.failed completed without a throwable"));
}
});
return promise.future();
}
/**
* Returns a Future that returns the result of this Future, if it is a success. If the value of this Future is a
* failure, the result of {@code that} Future is returned, if that is a success. If both Futures fail, the failure
* of this Future is returned.
*
* Example:
*
* Future<Integer> future = Future.of(() -> { throw new Error(); });
* Future<Integer> that = Future.of(() -> 1);
* Future<Integer> result = future.fallbackTo(that);
*
* // prints Some(1)
* result.onComplete(System.out::println);
*
*
* @param that A fallback future computation
* @return A new Future
* @throws NullPointerException if that is null
*/
default Future fallbackTo(Future extends T> that) {
Objects.requireNonNull(that, "that is null");
final Promise promise = Promise.make(executorService());
onComplete(t -> {
if (t.isSuccess()) {
promise.complete(t);
} else {
that.onComplete(alt -> {
if (alt.isSuccess()) {
promise.complete(alt);
} else {
promise.complete(t);
}
});
}
});
return promise.future();
}
/**
* Shortcut for {@code filterTry(predicate::test}.
*
* @param predicate A predicate
* @return A new {@code Future}
* @throws NullPointerException if {@code predicate} is null
*/
default Future filter(Predicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return filterTry(predicate::test);
}
/**
* Filters the result of this {@code Future} by calling {@link Try#filterTry(CheckedPredicate)}.
*
* @param predicate A checked predicate
* @return A new {@code Future}
* @throws NullPointerException if {@code predicate} is null
*/
default Future filterTry(CheckedPredicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
final Promise promise = Promise.make(executorService());
onComplete(result -> promise.complete(result.filterTry(predicate)));
return promise.future();
}
/**
* Returns the underlying exception of this Future, syntactic sugar for {@code future.getValue().map(Try::getCause)}.
*
* @return None if the Future is not completed yet. Returns Some(Throwable) if the Future was completed with a failure.
* @throws UnsupportedOperationException if the Future was successfully completed with a value
*/
default Option getCause() {
return getValue().map(Try::getCause);
}
/**
* Returns the value of the Future.
*
* @return {@code None}, if the Future is not yet completed or was cancelled, otherwise {@code Some(Try)}.
*/
Option> getValue();
/**
* Checks if this Future is completed, i.e. has a value.
*
* @return true, if the computation successfully finished, failed or was cancelled, false otherwise.
*/
boolean isCompleted();
/**
* Checks if this Future completed with a success.
*
* @return true, if this Future completed and is a Success, false otherwise.
*/
default boolean isSuccess() {
return getValue().map(Try::isSuccess).getOrElse(false);
}
/**
* Checks if this Future completed with a failure.
*
* @return true, if this Future completed and is a Failure, false otherwise.
*/
default boolean isFailure() {
return getValue().map(Try::isFailure).getOrElse(false);
}
/**
* Performs the action once the Future is complete.
*
* @param action An action to be performed when this future is complete.
* @return this Future
* @throws NullPointerException if {@code action} is null.
*/
Future onComplete(Consumer super Try> action);
/**
* Performs the action once the Future is complete and the result is a {@link Try.Failure}. Please note that the
* future is also a failure when it was cancelled.
*
* @param action An action to be performed when this future failed.
* @return this Future
* @throws NullPointerException if {@code action} is null.
*/
default Future onFailure(Consumer super Throwable> action) {
Objects.requireNonNull(action, "action is null");
return onComplete(result -> result.onFailure(action));
}
/**
* Performs the action once the Future is complete and the result is a {@link Try.Success}.
*
* @param action An action to be performed when this future succeeded.
* @return this Future
* @throws NullPointerException if {@code action} is null.
*/
default Future onSuccess(Consumer super T> action) {
Objects.requireNonNull(action, "action is null");
return onComplete(result -> result.onSuccess(action));
}
/**
* Handles a failure of this Future by returning another result.
*
* Example:
*
* // = "oh!"
* Future.of(() -> new Error("oh!")).recover(Throwable::getMessage);
*
*
* @param f A function which takes the exception of a failure and returns a new value.
* @return A new Future.
* @throws NullPointerException if {@code f} is null
*/
default Future recover(Function super Throwable, ? extends T> f) {
Objects.requireNonNull(f, "f is null");
final Promise promise = Promise.make(executorService());
onComplete(t -> promise.complete(t.recover(f)));
return promise.future();
}
/**
* Handles a failure of this Future by returning the result of another Future.
*
* Example:
*
* // = "oh!"
* Future.of(() -> { throw new Error("oh!"); }).recoverWith(x -> Future.of(x::getMessage));
*
*
* @param f A function which takes the exception of a failure and returns a new future.
* @return A new Future.
* @throws NullPointerException if {@code f} is null
*/
default Future recoverWith(Function super Throwable, ? extends Future extends T>> f) {
Objects.requireNonNull(f, "f is null");
final Promise promise = Promise.make(executorService());
onComplete(t -> {
if (t.isFailure()) {
Try.run(() -> f.apply(t.getCause()).onComplete(promise::complete)).onFailure(promise::failure);
} else {
promise.complete(t);
}
});
return promise.future();
}
/**
* Transforms this {@code Future}.
*
* @param f A transformation
* @param Type of transformation result
* @return An instance of type {@code U}
* @throws NullPointerException if {@code f} is null
*/
default U transform(Function super Future, ? extends U> f) {
Objects.requireNonNull(f, "f is null");
return f.apply(this);
}
/**
* Returns a tuple of this and that Future result.
*
* If this Future failed the result contains this failure. Otherwise the result contains that failure or
* a tuple of both successful Future results.
*
* @param that Another Future
* @param Result type of {@code that}
* @return A new Future that returns both Future results.
* @throws NullPointerException if {@code that} is null
*/
@SuppressWarnings("unchecked")
default Future> zip(Future extends U> that) {
Objects.requireNonNull(that, "that is null");
final Promise> promise = Promise.make(executorService());
onComplete(res1 -> {
if (res1.isFailure()) {
promise.complete((Try.Failure>) res1);
} else {
that.onComplete(res2 -> {
final Try> result = res1.flatMap(t -> res2.map(u -> Tuple.of(t, u)));
promise.complete(result);
});
}
});
return promise.future();
}
// -- Value & Monad implementation
default Future flatMap(Function super T, ? extends Future extends U>> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
return flatMapTry((CheckedFunction>) mapper::apply);
}
default Future flatMapTry(CheckedFunction super T, ? extends Future extends U>> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
final Promise promise = Promise.make(executorService());
onComplete((Try result) -> result.mapTry(mapper)
.onSuccess(promise::completeWith)
.onFailure(promise::failure)
);
return promise.future();
}
/**
* Performs the given {@code action} asynchronously hence this Future result becomes available.
* The {@code action} is not performed, if the result is a failure.
*
* @param action A {@code Consumer}
*/
@Override
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action, "action is null");
onComplete(result -> result.forEach(action));
}
/**
* Returns the value of the future. Waits for the result if necessary.
*
* @return The value of this future.
* @throws NoSuchElementException if the computation unexpectedly failed or was interrupted.
*/
// DEV-NOTE: A NoSuchElementException is thrown instead of the exception of the underlying Failure in order to
// be conform to Value#get
@Override
default T get() {
// is empty will block until result is available
if (isEmpty()) {
throw new NoSuchElementException("get on failed future");
} else {
return getValue().get().get();
}
}
/**
* Checks, if this future has a value.
*
* @return true, if this future succeeded with a value, false otherwise.
*/
@Override
default boolean isEmpty() {
// does not need to be synchronized, wait() has to check the completed state again
if (!isCompleted()) {
await();
}
return getValue().get().isEmpty();
}
/**
* A {@code Future} is single-valued.
*
* @return {@code true}
*/
@Override
default boolean isSingleValued() {
return true;
}
@Override
default Iterator iterator() {
return isEmpty() ? Iterator.empty() : Iterator.of(get());
}
@Override
default Future map(Function super T, ? extends U> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
return mapTry(mapper::apply);
}
default Future mapTry(CheckedFunction super T, ? extends U> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
final Promise promise = Promise.make(executorService());
onComplete(result -> promise.complete(result.mapTry(mapper)));
return promise.future();
}
default Future orElse(Future extends T> other) {
Objects.requireNonNull(other, "other is null");
final Promise promise = Promise.make(executorService());
onComplete(result -> {
if (result.isSuccess()) {
promise.complete(result);
} else {
other.onComplete(promise::complete);
}
});
return promise.future();
}
default Future orElse(Supplier extends Future extends T>> supplier) {
Objects.requireNonNull(supplier, "supplier is null");
final Promise promise = Promise.make(executorService());
onComplete(result -> {
if (result.isSuccess()) {
promise.complete(result);
} else {
supplier.get().onComplete(promise::complete);
}
});
return promise.future();
}
@Override
default Future peek(Consumer super T> action) {
Objects.requireNonNull(action, "action is null");
onSuccess(action::accept);
return this;
}
@Override
default String stringPrefix() {
return "Future";
}
}