io.vertx.core.Future Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package io.vertx.core;
import io.vertx.codegen.annotations.Fluent;
import io.vertx.codegen.annotations.GenIgnore;
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.impl.Utils;
import io.vertx.core.impl.future.CompositeFutureImpl;
import io.vertx.core.impl.future.FailedFuture;
import io.vertx.core.impl.future.SucceededFuture;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Represents the result of an action that may, or may not, have occurred yet.
*
*
* @author Tim Fox
*/
public interface Future extends AsyncResult {
/**
* Return a composite future, succeeded when all futures are succeeded, failed when any future is failed.
*
* The returned future fails as soon as one of {@code f1} or {@code f2} fails.
*
* @param f1 future
* @param f2 future
* @return the composite future
*/
static CompositeFuture all(Future> f1, Future> f2) {
return CompositeFutureImpl.all(f1, f2);
}
/**
* Like {@link #all(Future, Future)} but with 3 futures.
*/
static CompositeFuture all(Future> f1, Future> f2, Future> f3) {
return CompositeFutureImpl.all(f1, f2, f3);
}
/**
* Like {@link #all(Future, Future)} but with 4 futures.
*/
static CompositeFuture all(Future> f1, Future> f2, Future> f3, Future> f4) {
return CompositeFutureImpl.all(f1, f2, f3, f4);
}
/**
* Like {@link #all(Future, Future)} but with 5 futures.
*/
static CompositeFuture all(Future> f1, Future> f2, Future> f3, Future> f4, Future> f5) {
return CompositeFutureImpl.all(f1, f2, f3, f4, f5);
}
/**
* Like {@link #all(Future, Future)} but with 6 futures.
*/
static CompositeFuture all(Future> f1, Future> f2, Future> f3, Future> f4, Future> f5, Future> f6) {
return CompositeFutureImpl.all(f1, f2, f3, f4, f5, f6);
}
/**
* Like {@link #all(Future, Future)} but with a list of futures.
*
* When the list is empty, the returned future will be already completed.
*/
static CompositeFuture all(List extends Future>> futures) {
return CompositeFutureImpl.all(futures.toArray(new Future[0]));
}
/**
* Return a composite future, succeeded when any futures is succeeded, failed when all futures are failed.
*
* The returned future succeeds as soon as one of {@code f1} or {@code f2} succeeds.
*
* @param f1 future
* @param f2 future
* @return the composite future
*/
static CompositeFuture any(Future> f1, Future> f2) {
return CompositeFutureImpl.any(f1, f2);
}
/**
* Like {@link #any(Future, Future)} but with 3 futures.
*/
static CompositeFuture any(Future> f1, Future> f2, Future> f3) {
return CompositeFutureImpl.any(f1, f2, f3);
}
/**
* Like {@link #any(Future, Future)} but with 4 futures.
*/
static CompositeFuture any(Future> f1, Future> f2, Future> f3, Future> f4) {
return CompositeFutureImpl.any(f1, f2, f3, f4);
}
/**
* Like {@link #any(Future, Future)} but with 5 futures.
*/
static CompositeFuture any(Future> f1, Future> f2, Future> f3, Future> f4, Future> f5) {
return CompositeFutureImpl.any(f1, f2, f3, f4, f5);
}
/**
* Like {@link #any(Future, Future)} but with 6 futures.
*/
static CompositeFuture any(Future> f1, Future> f2, Future> f3, Future> f4, Future> f5, Future> f6) {
return CompositeFutureImpl.any(f1, f2, f3, f4, f5, f6);
}
/**
* Like {@link #any(Future, Future)} but with a list of futures.
*
* When the list is empty, the returned future will be already completed.
*/
static CompositeFuture any(List extends Future>> futures) {
return CompositeFutureImpl.any(futures.toArray(new Future[0]));
}
/**
* Return a composite future, succeeded when all futures are succeeded, failed when any future is failed.
*
* It always wait until all its futures are completed and will not fail as soon as one of {@code f1} or {@code f2} fails.
*
* @param f1 future
* @param f2 future
* @return the composite future
*/
static CompositeFuture join(Future> f1, Future> f2) {
return CompositeFutureImpl.join(f1, f2);
}
/**
* Like {@link #join(Future, Future)} but with 3 futures.
*/
static CompositeFuture join(Future> f1, Future> f2, Future> f3) {
return CompositeFutureImpl.join(f1, f2, f3);
}
/**
* Like {@link #join(Future, Future)} but with 4 futures.
*/
static CompositeFuture join(Future> f1, Future> f2, Future> f3, Future> f4) {
return CompositeFutureImpl.join(f1, f2, f3, f4);
}
/**
* Like {@link #join(Future, Future)} but with 5 futures.
*/
static CompositeFuture join(Future> f1, Future> f2, Future> f3, Future> f4, Future> f5) {
return CompositeFutureImpl.join(f1, f2, f3, f4, f5);
}
/**
* Like {@link #join(Future, Future)} but with 6 futures.
*/
static CompositeFuture join(Future> f1, Future> f2, Future> f3, Future> f4, Future> f5, Future> f6) {
return CompositeFutureImpl.join(f1, f2, f3, f4, f5, f6);
}
/**
* Like {@link #join(Future, Future)} but with a list of futures.
*
* When the list is empty, the returned future will be already completed.
*/
static CompositeFuture join(List extends Future>> futures) {
return CompositeFutureImpl.join(futures.toArray(new Future[0]));
}
/**
* Create a future that hasn't completed yet and that is passed to the {@code handler} before it is returned.
*
* @param handler the handler
* @param the result type
* @return the future.
*/
static Future future(Handler> handler) {
Promise promise = Promise.promise();
try {
handler.handle(promise);
} catch (Throwable e){
promise.tryFail(e);
}
return promise.future();
}
/**
* Create a succeeded future with a null result
*
* @param the result type
* @return the future
*/
static Future succeededFuture() {
return (Future) SucceededFuture.EMPTY;
}
/**
* Created a succeeded future with the specified result.
*
* @param result the result
* @param the result type
* @return the future
*/
static Future succeededFuture(T result) {
if (result == null) {
return succeededFuture();
} else {
return new SucceededFuture<>(result);
}
}
/**
* Create a failed future with the specified failure cause.
*
* @param t the failure cause as a Throwable
* @param the result type
* @return the future
*/
static Future failedFuture(Throwable t) {
return new FailedFuture<>(t);
}
/**
* Create a failed future with the specified failure message.
*
* @param failureMessage the failure message
* @param the result type
* @return the future
*/
static Future failedFuture(String failureMessage) {
return new FailedFuture<>(failureMessage);
}
/**
* Has the future completed?
*
* It's completed if it's either succeeded or failed.
*
* @return true if completed, false if not
*/
boolean isComplete();
/**
* Add a handler to be notified of the result.
*
* WARNING: this is a terminal operation.
* If several {@code handler}s are registered, there is no guarantee that they will be invoked in order of registration.
*
* @param handler the handler that will be called with the result
* @return a reference to this, so it can be used fluently
*/
@Fluent
Future onComplete(Handler> handler);
/**
* Add handlers to be notified on succeeded result and failed result.
*
* WARNING: this is a terminal operation.
* If several {@code handler}s are registered, there is no guarantee that they will be invoked in order of registration.
*
* @param successHandler the handler that will be called with the succeeded result
* @param failureHandler the handler that will be called with the failed result
* @return a reference to this, so it can be used fluently
*/
default Future onComplete(Handler successHandler, Handler failureHandler) {
return onComplete(ar -> {
if (successHandler != null && ar.succeeded()) {
successHandler.handle(ar.result());
} else if (failureHandler != null && ar.failed()) {
failureHandler.handle(ar.cause());
}
});
}
/**
* Add a handler to be notified of the succeeded result.
*
* WARNING: this is a terminal operation.
* If several {@code handler}s are registered, there is no guarantee that they will be invoked in order of registration.
*
* @param handler the handler that will be called with the succeeded result
* @return a reference to this, so it can be used fluently
*/
@Fluent
default Future onSuccess(Handler handler) {
return onComplete(handler, null);
}
/**
* Add a handler to be notified of the failed result.
*
* WARNING: this is a terminal operation.
* If several {@code handler}s are registered, there is no guarantee that they will be invoked in order of registration.
*
* @param handler the handler that will be called with the failed result
* @return a reference to this, so it can be used fluently
*/
@Fluent
default Future onFailure(Handler handler) {
return onComplete(null, handler);
}
/**
* The result of the operation. This will be null if the operation failed.
*
* @return the result or null if the operation failed.
*/
@Override
T result();
/**
* A Throwable describing failure. This will be null if the operation succeeded.
*
* @return the cause or null if the operation succeeded.
*/
@Override
Throwable cause();
/**
* Did it succeed?
*
* @return true if it succeded or false otherwise
*/
@Override
boolean succeeded();
/**
* Did it fail?
*
* @return true if it failed or false otherwise
*/
@Override
boolean failed();
/**
* Alias for {@link #compose(Function)}.
*/
default Future flatMap(Function> mapper) {
return compose(mapper);
}
/**
* Compose this future with a {@code mapper} function.
*
* When this future (the one on which {@code compose} is called) succeeds, the {@code mapper} will be called with
* the completed value and this mapper returns another future object. This returned future completion will complete
* the future returned by this method call.
*
* If the {@code mapper} throws an exception, the returned future will be failed with this exception.
*
* When this future fails, the failure will be propagated to the returned future and the {@code mapper}
* will not be called.
*
* @param mapper the mapper function
* @return the composed future
*/
default Future compose(Function> mapper) {
return compose(mapper, Future::failedFuture);
}
/**
* Handles a failure of this Future by returning the result of another Future.
* If the mapper fails, then the returned future will be failed with this failure.
*
* @param mapper A function which takes the exception of a failure and returns a new future.
* @return A recovered future
*/
default Future recover(Function> mapper) {
return compose(Future::succeededFuture, mapper);
}
/**
* Compose this future with a {@code successMapper} and {@code failureMapper} functions.
*
* When this future (the one on which {@code compose} is called) succeeds, the {@code successMapper} will be called with
* the completed value and this mapper returns another future object. This returned future completion will complete
* the future returned by this method call.
*
* When this future (the one on which {@code compose} is called) fails, the {@code failureMapper} will be called with
* the failure and this mapper returns another future object. This returned future completion will complete
* the future returned by this method call.
*
* If any mapper function throws an exception, the returned future will be failed with this exception.
*
* @param successMapper the function mapping the success
* @param failureMapper the function mapping the failure
* @return the composed future
*/
Future compose(Function> successMapper, Function> failureMapper);
/**
* Transform this future with a {@code mapper} functions.
*
* When this future (the one on which {@code transform} is called) completes, the {@code mapper} will be called with
* the async result and this mapper returns another future object. This returned future completion will complete
* the future returned by this method call.
*
* If any mapper function throws an exception, the returned future will be failed with this exception.
*
* @param mapper the function mapping the future
* @return the transformed future
*/
Future transform(Function, Future> mapper);
/**
* Compose this future with a {@code function} that will be always be called.
*
* When this future (the one on which {@code eventually} is called) completes, the {@code function} will be called
* and this mapper returns another future object. This returned future completion will complete the future returned
* by this method call with the original result of the future.
*
*
The outcome of the future returned by the {@code function} will not influence the nature
* of the returned future.
*
* @param function the function returning the future.
* @return the composed future
* @deprecated instead use {@link #eventually(Supplier)}, this method will be removed in Vert.x 5
*/
@Deprecated
Future eventually(Function> function);
/**
* Compose this future with a {@code supplier} that will be always be called.
*
* When this future (the one on which {@code eventually} is called) completes, the {@code supplier} will be called
* and this mapper returns another future object. This returned future completion will complete the future returned
* by this method call with the original result of the future.
*
*
The outcome of the future returned by the {@code supplier} will not influence the nature
* of the returned future.
*
* @param supplier the function returning the future.
* @return the composed future
*/
default Future eventually(Supplier> supplier) {
return eventually(v -> supplier.get());
}
/**
* Apply a {@code mapper} function on this future.
*
* When this future succeeds, the {@code mapper} will be called with the completed value and this mapper
* returns a value. This value will complete the future returned by this method call.
*
* If the {@code mapper} throws an exception, the returned future will be failed with this exception.
*
* When this future fails, the failure will be propagated to the returned future and the {@code mapper}
* will not be called.
*
* @param mapper the mapper function
* @return the mapped future
*/
Future map(Function mapper);
/**
* Map the result of a future to a specific {@code value}.
*
* When this future succeeds, this {@code value} will complete the future returned by this method call.
*
* When this future fails, the failure will be propagated to the returned future.
*
* @param value the value that eventually completes the mapped future
* @return the mapped future
*/
Future map(V value);
/**
* Map the result of a future to {@code null}.
*
* This is a conveniency for {@code future.map((T) null)} or {@code future.map((Void) null)}.
*
* When this future succeeds, {@code null} will complete the future returned by this method call.
*
* When this future fails, the failure will be propagated to the returned future.
*
* @return the mapped future
*/
@Override
default Future mapEmpty() {
return (Future) AsyncResult.super.mapEmpty();
}
/**
* Apply a {@code mapper} function on this future.
*
* When this future fails, the {@code mapper} will be called with the completed value and this mapper
* returns a value. This value will complete the future returned by this method call.
*
* If the {@code mapper} throws an exception, the returned future will be failed with this exception.
*
* When this future succeeds, the result will be propagated to the returned future and the {@code mapper}
* will not be called.
*
* @param mapper the mapper function
* @return the mapped future
*/
Future otherwise(Function mapper);
/**
* Map the failure of a future to a specific {@code value}.
*
* When this future fails, this {@code value} will complete the future returned by this method call.
*
* When this future succeeds, the result will be propagated to the returned future.
*
* @param value the value that eventually completes the mapped future
* @return the mapped future
*/
Future otherwise(T value);
/**
* Map the failure of a future to {@code null}.
*
* This is a convenience for {@code future.otherwise((T) null)}.
*
* When this future fails, the {@code null} value will complete the future returned by this method call.
*
* When this future succeeds, the result will be propagated to the returned future.
*
* @return the mapped future
*/
default Future otherwiseEmpty() {
return (Future) AsyncResult.super.otherwiseEmpty();
}
/**
* Invokes the given {@code handler} upon completion.
*
* If the {@code handler} throws an exception, the returned future will be failed with this exception.
*
* @param handler invoked upon completion of this future
* @return a future completed after the {@code handler} has been invoked
*/
default Future andThen(Handler> handler) {
return transform(ar -> {
handler.handle(ar);
return (Future) ar;
});
}
/**
* Guard the control flow of this future with an expectation.
*
* When the future is completed with a success, the {@code expectation} is called with the result, when the expectation
* returns {@code false} the returned future is failed, otherwise the future is completed with the same result.
*
* Expectations are usually lambda expressions:
*
* return future.expecting(response -> response.statusCode() == 200);
*
* {@link Expectation} instances can also be used:
*
* future = future.expecting(HttpResponseExpectation.SC_OK);
*
*
* @param expectation the expectation
* @return a future succeeded with the result or failed when the expectation returns false
*/
Future expecting(Expectation super T> expectation);
/**
* Returns a future succeeded or failed with the outcome of this future when it happens before the timeout fires. When
* the timeout fires before, the future is failed with a {@link java.util.concurrent.TimeoutException}, guaranteeing
* the returned future to complete within the specified {@code delay}.
*
* @param delay the delay
* @param unit the unit of the delay
* @return the timeout future
*/
Future timeout(long delay, TimeUnit unit);
/**
* Bridges this Vert.x future to a {@link CompletionStage} instance.
*
* The {@link CompletionStage} handling methods will be called from the thread that resolves this future.
*
* @return a {@link CompletionStage} that completes when this future resolves
*/
@GenIgnore
default CompletionStage toCompletionStage() {
CompletableFuture completableFuture = new CompletableFuture<>();
onComplete(ar -> {
if (ar.succeeded()) {
completableFuture.complete(ar.result());
} else {
completableFuture.completeExceptionally(ar.cause());
}
});
return completableFuture;
}
/**
* Bridges a {@link CompletionStage} object to a Vert.x future instance.
*
* The Vert.x future handling methods will be called from the thread that completes {@code completionStage}.
*
* @param completionStage a completion stage
* @param the result type
* @return a Vert.x future that resolves when {@code completionStage} resolves
*/
@GenIgnore
static Future fromCompletionStage(CompletionStage completionStage) {
Promise promise = Promise.promise();
completionStage.whenComplete((value, err) -> {
if (err != null) {
promise.fail(err);
} else {
promise.complete(value);
}
});
return promise.future();
}
/**
* Bridges a {@link CompletionStage} object to a Vert.x future instance.
*
* The Vert.x future handling methods will be called on the provided {@code context}.
*
* @param completionStage a completion stage
* @param context a Vert.x context to dispatch to
* @param the result type
* @return a Vert.x future that resolves when {@code completionStage} resolves
*/
@GenIgnore
static Future fromCompletionStage(CompletionStage completionStage, Context context) {
Promise promise = ((ContextInternal) context).promise();
completionStage.whenComplete((value, err) -> {
if (err != null) {
promise.fail(err);
} else {
promise.complete(value);
}
});
return promise.future();
}
/**
* Park the current thread until the {@code future} is completed, when the future
* is completed the thread is un-parked and
*
*
* - the result value is returned when the future was completed with a result
* - otherwise, the failure is thrown
*
*
* This method must be called from a virtual thread.
*
* @param future the future to await
* @return the result
* @throws IllegalStateException when called from an event-loop thread or a non Vert.x thread
*/
static T await(Future future) {
io.vertx.core.impl.WorkerExecutor executor = io.vertx.core.impl.WorkerExecutor.unwrapWorkerExecutor();
io.vertx.core.impl.WorkerExecutor.TaskController cont = executor.current();
future.onComplete(ar -> cont.resume());
try {
cont.suspendAndAwaitResume();
} catch (InterruptedException e) {
Utils.throwAsUnchecked(e);
return null;
}
if (future.succeeded()) {
return future.result();
} else {
Utils.throwAsUnchecked(future.cause());
return null;
}
}
}