All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javascalautils.concurrent.Future Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2015 Peter Nerg
 *
 * 

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * *

http://www.apache.org/licenses/LICENSE-2.0 * *

Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package javascalautils.concurrent; import javascalautils.*; import java.time.Duration; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import static javascalautils.TryCompanion.*; /** * A Future that will hold the result of an asynchronous computation.
* The Future is paired with an instance of {@link Promise} which is also the way to get hold of a * Future.
* One can see a Future as a holder for a value-to-be, not yet available but accessible sometime in * the future.
* The preferred way to get hold of the value-to-be is to register a listener on any of the provided * listener types since this allows for asynchronous non-blocking operations. * *

    *
  • {@link #onComplete(Consumer)} *
  • {@link #onSuccess(Consumer)} *
  • {@link #onFailure(Consumer)} *
* * It is possible to register multiple listeners to several/same listener type.
* One can register a listener both before and after a Future has been completed.
* Should the Future already be completed when the listener is added it is fired immediately.
* The guarantee is that a listener is only fired once.
*
* All operations on this class are non-blocking, i.e. allowing for proper asynchronous/non-blocking * programming patterns.
* With one exception, for situations where it is necessary to maintain synchronous/blocking * behavior the method {@link #result(long, TimeUnit)} is provided.
*
* For real short and consistent programming one can use the {@link #apply(ThrowableFunction0)} * method to provide a function that will be executed in the future.
* The result of the function will be reported to the Future returned by the method. * *
* *
 * Future<Integer> resultSuccess = Future.apply(() -> 9 / 3); // The Future will at some point contain: Success(3)
 * Future<Integer> resultFailure = Future.apply(() -> 9 / 0); // The Future will at some point contain: Failure(ArithmeticException)
 * 
* *
* * @author Peter Nerg * @since 1.2 * @param The type this Future will hold as result */ public interface Future { /** * Allows for easy creation of asynchronous computations that will be executed in the future.
* The method will use the {@link Executors#getDefault()} method to get hold of the default {@link * Executor} to use for executing the provided job.
* Simple examples: * *
* *
   * Future<Integer> resultSuccess = Future.apply(() -> 9 / 3); // The Future will at some point contain: Success(3)
   * Future<Integer> resultFailure = Future.apply(() -> 9 / 0); // The Future will at some point contain: Failure(ArithmeticException)
   * 
* *
* * @param The type for the Future * @param function The function to render either the value T or raise an exception. * @return The future that will hold the result provided by the function * @since 1.3 */ static Future apply(ThrowableFunction0 function) { return apply(function, Executors.getDefault()); } /** * Allows for easy creation of asynchronous computations that will be executed in the future.
* The method will use the provided {@link Executor} for executing the provided job.
* Simple examples: * *
* *
   * Future<Integer> resultSuccess = Future.apply(() -> 9 / 3, someExecutor); // The Future will at some point contain: Success(3)
   * Future<Integer> resultFailure = Future.apply(() -> 9 / 0, someExecutor); // The Future will at some point contain: Failure(ArithmeticException)
   * 
* *
* * @param The type for the Future * @param function The function to render either the value T or raise an exception. * @param executor The executor to use to compute/execute the Future holding the provided function * @return The future that will hold the result provided by the function * @since 1.4 */ static Future apply(ThrowableFunction0 function, Executor executor) { return executor.execute(promise -> promise.complete(Try(function))); } /** * Creates a failed Future with the provided Throwable. * * @param The type for the Future * @param throwable The throwable to complete the Future with. * @return The completed Future holding the provided Throwable * @since 1.5 */ static Future failed(Throwable throwable) { return fromTry(Failure(throwable)); } /** * Creates a successful Future with the provided value. * * @param The type for the Future * @param value The value to complete the Future with. * @return The completed Future holding the provided value * @since 1.5 */ static Future successful(T value) { return fromTry(Success(value)); } /** * Creates a completed Future with the provided Try.
* The Future can therefore be either {@link Success} or {@link Failure}. * * @param The type for the Future * @param result The {@link Success}/{@link Failure} to complete the Future with. * @return The completed Future holding the provided result * @since 1.5 */ static Future fromTry(Try result) { return new FutureImpl().complete(result); } /** * Turns a Stream of Futures into a single Future containing a Stream with all the results from * the Futures.
* Allows for easy management of multiple Futures.
* Note, should any Future in the Stream fail the entire sequence fails.
* An empty input Stream will result in a Future containing an empty result Stream.
* * @param The type for the Stream in the resulting Future * @param stream The Stream with Futures * @return A single Future containing the Stream of results * @since 1.5 */ static Future> sequence(Stream> stream) { return traverse(stream, f -> f); } /** * Takes a Stream of values and applies the provided function to them in parallel resulting in a * Future containing a Stream with the mapped values.
* Can be used to run a mapping operation in parallel e.g.: * *
* *
   * import static javascalautils.FutureCompanion.Future;
   *
   * Stream<String> stream = ...; // Stream with strings
   * Future<Stream<Integer>> future = Future.traverse(stream, v -> Future(() -> v.length()));
   * 
* *
* * @param The type for the input Stream * @param The type for the Stream in the resulting Future * @param stream The Stream with values * @param function The function to be applied to all values of the Stream * @return A single Future containing the Stream of results * @since 1.5 */ static Future> traverse(Stream stream, Function> function) { // map all Future to Future> Stream>> mappedStream = stream.map(v -> function.apply(v)).map(f -> f.map(v -> Stream.of(v))); // create the initial (complete) Future used by the reduction // the Future is completed with an empty stream // this is used as the base for the reduction, it will also be the result in case the input // stream was empty Future> initial = successful(Stream.empty()); // now it's a simple reduction of the stream // for each found Future> we perform a flatMap with a map of the left/right Future // creating a new single Future return mappedStream.reduce( initial, (f1, f2) -> f1.flatMap(f1v -> f2.map(f2v -> Stream.concat(f1v, f2v)))); } /** * Check if this Future is completed, with a value or an exception. * * @return true if completed, false otherwise. */ boolean isCompleted(); /** * The current (completed or not) value of the future.
* This is a non-blocking method, it will return the current state/value of the Future.
* There are three possible outcomes: * *
    *
  • The future has not been completed -> {@link None} is returned *
  • The execution of the Future was successful -> {@link Some} with a {@link Success} * containing the value of the executed job *
  • The execution failed and an exception was reported -> {@link Some} with a {@link * Failure} containing the Throwable *
* * @return An {@link Option} with the result. */ Option> value(); /** * Register a handler to be invoked if the Future gets completed with an exception.
* If the Future has already been completed the notification will happen in the current thread. *
* Multiple handlers can be registered, without any guarantee of notification order.
* Each individual event handler will only be invoked once.
* * @param failureHandler Consumer to invoke. */ void onFailure(Consumer failureHandler); /** * Register a handler to be invoked if the Future gets completed with a value.
* If the Future has already been completed the notification will happen in the current thread. *
* Multiple handlers can be registered, without any guarantee of notification order.
* Each individual event handler will only be invoked once.
* * @param successHandler Consumer to invoke. */ void onSuccess(Consumer successHandler); /** * Register a handler to be invoked if the Future gets completed with a value or a failure.
* If the Future has already been completed the notification will happen in the current thread. *
* Multiple handlers can be registered, without any guarantee of notification order.
* Each individual event handler will only be invoked once.
* * @param completeHandler Consumer to invoke. */ void onComplete(Consumer> completeHandler); /** * Asynchronously processes the value in the Future once it is available.
* Only successful Futures are reported to the consumer.
* This is pretty much the same as {@link #onSuccess(Consumer)} but is here for completion keeping * a consistent look and feel. * * @param consumer The consumer to digest the result */ void forEach(Consumer consumer); /** * Creates a new {@link Future} that will hold the mapped successful value of this instance once * it is completed.
* Unsuccessful Futures will not be mapped, they are kept unsuccessful as they are. * * @param The type for the value held by the mapped future * @param function The function to apply * @return The mapped Future */ Future map(ThrowableFunction1 function); /** * Creates a new {@link Future} that will hold the mapped successful value of this instance once * it is completed.
* Unsuccessful Futures will not be mapped, they are kept unsuccessful as they are. * * @param The type for the value held by the mapped future * @param function The function to apply * @return The mapped Future */ Future flatMap(ThrowableFunction1> function); /** * Creates a new {@link Future} that will filter the successful value of this instance once it is * completed.
* The possible outcomes are: * *
    *
  • This Future is successful -> predicate matches -> The filtered future is * completed with the value. *
  • This Future is successful -> predicate fails -> The filtered future is failed * with NoSuchElementException. *
  • This Future is failure -> The failure is passed on to the filtered Future. *
* * @param predicate The predicate to apply * @return The filtered Future */ Future filter(Predicate predicate); /** * Creates a new {@link Future} that will hold the transformed successful value of this instance * once it is completed.
* Successful futures are transformed with the onSuccess function and unsuccessful/failure * futures are transformed with onFailure. * * @param The type for the value held by the mapped future * @param onSuccess The function to apply on a 'successful' result * @param onFailure The function to apply on a 'failure' result * @return The mapped Future * @since 1.3 */ Future transform( ThrowableFunction1 onSuccess, ThrowableFunction1 onFailure); /** * Creates a new {@link Future} that in case this {@link Future} is a 'failure' will apply * the function to recover the 'failure' to a 'success'.
* Should this {@link Future} be a 'success' the value is propagated as-is.
* E.g. * *
* *
   * Future<String> future = ...
   * Future<String> recovered = future.recover(t -> t.getMessage());
   * 
* *
* * In case of 'future' being successful then that value is passed on to 'recovered', in case of * failure then the recover function kicks in and returns the message from the throwable. * * @param recoverFunction The function to apply in case of a 'failure' * @return The recovered Future * @since 1.3 */ Future recover(ThrowableFunction1 recoverFunction); /** * Blocks and waits for this Future to complete.
* Returns the result of a successful Future or throws the exception in case of a failure.
* The methods blocks for at most the provided duration.
* If the Future is already completed the method returns immediately. * * @param duration The duration to block * @param timeUnit The unit for the duration * @return The result in case successful * @throws Throwable The error reported in case of a failure * @throws TimeoutException In case the waiting time is passed */ T result(long duration, TimeUnit timeUnit) throws Throwable, TimeoutException; /** * Blocks and waits for this Future to complete.
* Returns the result of a successful Future or throws the exception in case of a failure.
* The methods blocks for at most the provided duration.
* If the Future is already completed the method returns immediately. * * @param duration The duration to block * @return The result in case successful * @throws Throwable The error reported in case of a failure * @throws TimeoutException In case the waiting time is passed * @since 1.8 */ default T result(Duration duration) throws Throwable, TimeoutException { Validator.requireNonNull(duration, "Null is not a valid Duration"); return result(duration.toMillis(), TimeUnit.MILLISECONDS); } /** * Blocks and waits for this Future to complete.
* As opposed to {@link #result(long, TimeUnit) result} this method will not return the value or * throw the exception of the Future.
* The method will upon completion returns this.
* The purpose of the method is to provide a blocking mechanism waiting for the Future to * complete.
* Any action on the Future's result is then left to the developer to manage. * * @param duration The duration to block * @param timeUnit The unit for the duration * @return this if the Future completes within the specified time * @throws TimeoutException In case the waiting time is passed * @throws InterruptedException In case the thread gets interrupted during the wait * @since 1.8 */ Future ready(long duration, TimeUnit timeUnit) throws TimeoutException, InterruptedException; /** * Blocks and waits for this Future to complete.
* As opposed to {@link #result(long, TimeUnit) result} this method will not return the value or * throw the exception of the Future.
* The method will upon completion returns this.
* The purpose of the method is to provide a blocking mechanism waiting for the Future to * complete.
* Any action on the Future's result is then left to the developer to manage. * * @param duration The duration to block * @return this if the Future completes within the specified time * @throws TimeoutException In case the waiting time is passed * @throws InterruptedException In case the thread gets interrupted during the wait * @since 1.8 */ default Future ready(Duration duration) throws TimeoutException, InterruptedException { Validator.requireNonNull(duration, "Null is not a valid Duration"); return ready(duration.toMillis(), TimeUnit.MILLISECONDS); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy