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

ratpack.exec.Promise Maven / Gradle / Ivy

There is a newer version: 2.0.0-rc-1
Show newest version
/*
 * Copyright 2014 the original author or authors.
 *
 * 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 ratpack.exec;

import com.google.common.cache.LoadingCache;
import ratpack.api.NonBlocking;
import ratpack.exec.internal.CachingUpstream;
import ratpack.exec.internal.DefaultExecution;
import ratpack.exec.internal.DefaultOperation;
import ratpack.exec.internal.DefaultPromise;
import ratpack.exec.util.Promised;
import ratpack.func.*;
import ratpack.util.Exceptions;

import java.time.Duration;
import java.util.Objects;

import static ratpack.func.Action.ignoreArg;

/**
 * A promise for a single value.
 * 

* A promise is a representation of a value which will become available later. * Methods such as {@link #map(Function)}, {@link #flatMap(Function)}, {@link #cache()} etc.) allow a pipeline of “operations” to be specified, * that the value will travel through as it becomes available. * Such operations are implemented via the {@link #transform(Function)} method. * Each operation returns a new promise object, not the original promise object. *

* To create a promise, use the {@link Promise#async(Upstream)} method (or one of the variants such as {@link Promise#sync(Factory)}. * To test code that uses promises, use the {@link ratpack.test.exec.ExecHarness}. *

* The promise is not “activated” until the {@link #then(Action)} method is called. * This method terminates the pipeline, and receives the final value. *

* Promise objects are multi use. * Every promise pipeline has a value producing function at its start. * Activating a promise (i.e. calling {@link #then(Action)}) invokes the function. * The {@link #cache()} operation can be used to change this behaviour. * * @param the type of promised value */ @SuppressWarnings("JavadocReference") public interface Promise { /** * Creates a promise for value that will be produced asynchronously. *

* The {@link Upstream#connect(Downstream)} method of the given upstream will be invoked every time the value is requested. * This method should propagate the value (or error) to the given downstream object when it is available. * *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String[] args) throws Exception {
   *     String value = ExecHarness.yieldSingle(e ->
   *       Promise.async(down ->
   *         new Thread(() -> {
   *           down.success("foo");
   *         }).start()
   *       )
   *     ).getValueOrThrow();
   *
   *     assertEquals(value, "foo");
   *   }
   * }
   * }
* * @param upstream the producer of the value * @param the type of promised value * @return a promise for the asynchronously created value * @see Upstream * @see #sync(Factory) * @see #value(Object) * @see #error(Throwable) * @since 1.3 */ static Promise async(Upstream upstream) { return new DefaultPromise<>(DefaultExecution.upstream(upstream)); } /** * Creates a promise for value, synchronously, produced by the given factory. *

* The given factory will be invoked every time that the value is requested. * If the factory throws an exception, the promise will convey that exception. * *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String[] args) throws Exception {
   *     String value = ExecHarness.yieldSingle(e ->
   *       Promise.sync(() -> "foo")
   *     ).getValueOrThrow();
   *
   *     assertEquals(value, "foo");
   *   }
   * }
   * }
* *

* This method is often used to when a method needs to return a promise, but can produce its value synchronously. * * @param factory the producer of the value * @param the type of promised value * @return a promise for the result of the factory * @see #async(Upstream) * @see #value(Object) * @see #error(Throwable) * @since 1.3 */ static Promise sync(Factory factory) { return async(down -> { T t; try { t = factory.create(); } catch (Exception e) { down.error(e); return; } down.success(t); }); } /** * Creates a promise for the given item. *

* The given item will be used every time that the value is requested. * *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String[] args) throws Exception {
   *     String value = ExecHarness.yieldSingle(e ->
   *       Promise.value("foo")
   *     ).getValueOrThrow();
   *
   *     assertEquals(value, "foo");
   *   }
   * }
   * }
* * @param t the promised value * @param the type of promised value * @return a promise for the given item * @see #async(Upstream) * @see #sync(Factory) * @see #error(Throwable) */ static Promise value(T t) { return async(down -> down.success(t)); } /** * Creates a failed promise with the given error. *

* The given error will be used every time that the value is requested. * *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import static org.junit.Assert.assertSame;
   *
   * public class Example {
   *   public static void main(String[] args) throws Exception {
   *     Exception exception = new Exception();
   *     Throwable error = ExecHarness.yieldSingle(e ->
   *       Promise.error(exception)
   *     ).getThrowable();
   *
   *     assertSame(exception, error);
   *   }
   * }
   * }
* * @param t the error * @param the type of promised value * @return a failed promise * @see #async(Upstream) * @see #sync(Factory) * @see #value(Object) */ static Promise error(Throwable t) { return async(down -> down.error(t)); } /** * Deprecated. * * @param upstream the producer of the value * @param the type of promised value * @return a promise for the asynchronously created value * @deprecated replaced by {@link #async(Upstream)} */ @Deprecated static Promise of(Upstream upstream) { return async(upstream); } /** * Deprecated. * * @param factory the producer of the value * @param the type of promised value * @return a promise for the result of the factory * @deprecated replaced by {@link #sync(Factory)}} */ @Deprecated static Promise ofLazy(Factory factory) { return sync(factory); } /** * Specifies what should be done with the promised object when it becomes available. *

* Important: this method can only be used from a Ratpack managed compute thread. * If it is called on a non Ratpack managed compute thread it will immediately throw an {@link ExecutionException}. * * @param then the receiver of the promised value * @throws ExecutionException if not called on a Ratpack managed compute thread */ void then(Action then); /** * A low level hook for consuming the promised value. *

* It is generally preferable to use {@link #then(Action)} over this method. * * @param downstream the downstream consumer */ void connect(Downstream downstream); /** * Apply a custom transform to this promise. *

* This method is the basis for the standard operations of this interface, such as {@link #map(Function)}. * The following is a non generic implementation of a map that converts the value to upper case. *

{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     ExecResult result = ExecHarness.yieldSingle(c ->
   *       Promise.value("foo")
   *         .transform(up -> down ->
   *           up.connect(down.onSuccess(value -> {
   *             try {
   *               down.success(value.toUpperCase());
   *             } catch (Throwable e) {
   *               down.error(e);
   *             }
   *           }))
   *         )
   *     );
   *
   *     assertEquals("FOO", result.getValue());
   *   }
   * }
   * }
*

* The “upstreamTransformer” function takes an upstream data source, and returns another upstream that wraps it. * It is typical for the returned upstream to invoke the {@link Upstream#connect(Downstream)} method of the given upstream during its connect method. *

* For more examples of transform implementations, please see the implementations of the methods of this interface. * * @param upstreamTransformer a function that returns a new upstream, typically wrapping the given upstream argument * @param the type of item emitted by the transformed upstream * @return a new promise */ Promise transform(Function, ? extends Upstream> upstreamTransformer); /** * Specifies the action to take if the an error occurs trying to produce the promised value, that the given predicate applies to. *

* If the given action throws an exception, the original exception will be rethrown with the exception thrown * by the action added to the suppressed exceptions list. * * @param predicate the predicate to test against the error * @param errorHandler the action to take if an error occurs * @return A promise for the successful result * @since 1.1 */ default Promise onError(Predicate predicate, Action errorHandler) { return transform(up -> down -> up.connect(down.onError(throwable -> { if (predicate.apply(throwable)) { try { errorHandler.execute(throwable); } catch (Throwable e) { if (e != throwable) { e.addSuppressed(throwable); } down.error(e); return; } down.complete(); } else { down.error(throwable); } })) ); } /** * Specifies the action to take if the an error of the given type occurs trying to produce the promised value. * *

{@code
   * import ratpack.http.TypedData;
   * import ratpack.test.embed.EmbeddedApp;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     EmbeddedApp.fromHandler(ctx ->
   *         ctx.getRequest().getBody()
   *           .map(TypedData::getText)
   *           .map(t -> {
   *             if (t.equals("1")) {
   *               throw new IllegalArgumentException("validation error!");
   *             } else {
   *               throw new RuntimeException("some other error!");
   *             }
   *           })
   *           .onError(IllegalArgumentException.class, e -> ctx.render("the value is invalid"))
   *           .onError(e -> ctx.render("unknown error: " + e.getMessage()))
   *           .then(t -> ctx.render("ok"))
   *     ).test(httpClient -> {
   *       assertEquals(httpClient.requestSpec(r -> r.getBody().text("0")).postText(), "unknown error: some other error!");
   *       assertEquals(httpClient.requestSpec(r -> r.getBody().text("1")).postText(), "the value is invalid");
   *     });
   *   }
   * }
   * }
*

* If the given action throws an exception, the original exception will be rethrown with the exception thrown * by the action added to the suppressed exceptions list. * * @param errorType the type of exception to handle with the given action * @param errorHandler the action to take if an error occurs * @param the type of exception to handle with the given action * @return A promise for the successful result * @since 1.1 */ default Promise onError(Class errorType, Action errorHandler) { return onError(errorType::isInstance, t -> errorHandler.execute(errorType.cast(t))); } /** * Specifies the action to take if the an error occurs trying to produce the promised value. *

* If the given action throws an exception, the original exception will be rethrown with the exception thrown * by the action added to the suppressed exceptions list. * * @param errorHandler the action to take if an error occurs * @return A promise for the successful result */ default Promise onError(Action errorHandler) { return onError(Predicate.TRUE, errorHandler); } /** * Consume the promised value as a {@link Result}. *

* This method is an alternative to {@link #then(Action)} and {@link #onError(Action)}. * * @param resultHandler the consumer of the result */ default void result(Action> resultHandler) { connect(new Downstream() { @Override public void success(T value) { try { resultHandler.execute(ExecResult.of(Result.success(value))); } catch (Throwable e) { DefaultPromise.throwError(e); } } @Override public void error(Throwable throwable) { try { resultHandler.execute(ExecResult.of(Result.error(throwable))); } catch (Throwable e) { DefaultPromise.throwError(e); } } @Override public void complete() { try { resultHandler.execute(ExecResult.complete()); } catch (Throwable e) { DefaultPromise.throwError(e); } } }); } /** * Transforms the promised value by applying the given function to it. *

{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     ExecResult result = ExecHarness.yieldSingle(c ->
   *         Promise.value("foo")
   *           .map(String::toUpperCase)
   *           .map(s -> s + "-BAR")
   *     );
   *
   *     assertEquals("FOO-BAR", result.getValue());
   *   }
   * }
   * }
* * @param transformer the transformation to apply to the promised value * @param the type of the transformed object * @return a promise for the transformed value */ default Promise map(Function transformer) { return transform(up -> down -> up.connect( down.onSuccess(value -> { try { O apply = transformer.apply(value); down.success(apply); } catch (Throwable e) { down.error(e); } }) ) ); } /** * Transforms the promised value by applying the given function to it, if it satisfies the predicate. * *
{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     ExecResult result = ExecHarness.yieldSingle(c ->
   *         Promise.value("foo")
   *           .mapIf(s -> s.contains("f"), String::toUpperCase)
   *           .mapIf(s -> s.contains("f"), s -> s + "-BAR")
   *     );
   *
   *     assertEquals("FOO", result.getValue());
   *   }
   * }
   * }
* * @param predicate the condition to satisfy in order to be transformed * @param transformer the transformation to apply to the promised value * @return a promise * @since 1.4 */ default Promise mapIf(Predicate predicate, Function transformer) { return map(t -> predicate.apply(t) ? transformer.apply(t) : t); } /** * Like {@link #map(Function)}, but performs the transformation on a blocking thread. *

* This is simply a more convenient form of using {@link Blocking#get(Factory)} and {@link #flatMap(Function)}. * * @param transformer the transformation to apply to the promised value, on a blocking thread * @param the type of the transformed object * @return a promise for the transformed value */ default Promise blockingMap(Function transformer) { return flatMap(t -> Blocking.get(() -> transformer.apply(t))); } /** * Executes the given action with the promise value, on a blocking thread. *

* Similar to {@link #blockingMap(Function)}, but does not provide a new value. * This can be used to do something with the value, without terminating the promise. * * @param action the action to to perform with the value, on a blocking thread * @return a promise for the same value given to the action */ default Promise blockingOp(Action action) { return flatMap(t -> Blocking.op(action.curry(t)).map(() -> t)); } /** * Deprecated. *

* Use {@link #replace(Promise)}. * * @param next the promise to replace {@code this} with * @param the type of value provided by the replacement promise * @return a promise * @deprecated replaced by {@link #replace(Promise)} as of 1.1.0 */ @Deprecated default Promise next(Promise next) { return flatMap(in -> next); } /** * Executes the provided, potentially asynchronous, {@link Action} with the promised value as input. *

* This method can be used when needing to perform an action with the promised value, without substituting the promised value. * That is, the exact same object provided to the given action will be propagated downstream. *

* The given action is executed within an {@link Operation}, allowing it to perform asynchronous work. * *

{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   *
   * import com.google.common.collect.Lists;
   *
   * import java.util.concurrent.TimeUnit;
   * import java.util.Arrays;
   * import java.util.List;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     List events = Lists.newLinkedList();
   *     ExecHarness.runSingle(c ->
   *       Promise.value("foo")
   *        .next(v ->
   *          Promise.value(v) // may be async
   *            .map(String::toUpperCase)
   *            .then(events::add)
   *        )
   *        .then(events::add)
   *     );
   *     assertEquals(Arrays.asList("FOO", "foo"), events);
   *   }
   * }
   * }
* * @param action the action to execute with the promised value * @return a promise for the original value * @see #nextOp(Function) * @since 1.1 */ default Promise next(@NonBlocking Action action) { return nextOp(v -> Operation.of(() -> action.execute(v) ) ); } /** * Executes the operation returned by the given function. *

* This method can be used when needing to perform an operation returned by another object, based on the promised value. * *

{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   * import ratpack.exec.Operation;
   *
   * import com.google.common.collect.Lists;
   *
   * import java.util.concurrent.TimeUnit;
   * import java.util.Arrays;
   * import java.util.List;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *
   *   public static class CaseService {
   *     public Operation toUpper(String value, List values) {
   *       return Operation.of(() -> values.add(value.toUpperCase()));
   *     }
   *   }
   *
   *   public static void main(String... args) throws Exception {
   *     CaseService service = new CaseService();
   *     List events = Lists.newLinkedList();
   *
   *     ExecHarness.runSingle(c ->
   *       Promise.value("foo")
   *        .nextOp(v -> service.toUpper(v, events))
   *        .then(events::add)
   *     );
   *
   *     assertEquals(Arrays.asList("FOO", "foo"), events);
   *   }
   * }
   * }
* * @param function a function that returns an operation that acts on the promised value * @return a promise for the original value * @see #next(Action) * @since 1.1 */ default Promise nextOp(Function function) { return transform(up -> down -> up.connect( down.onSuccess(value -> function.apply(value) .onError(down::error) .then(() -> down.success(value) ) ) ) ); } /** * Replaces {@code this} promise with the provided promise for downstream subscribers. *

* This is simply a more convenient form of {@link #flatMap(Function)}, where the given promise is returned. * This method can be used when a subsequent operation on a promise isn't dependent on the actual promised value. *

* If the upstream promise fails, its error will propagate downstream and the given promise will never be subscribed to. * *

{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   private static String value;
   *
   *   public static void main(String... args) throws Exception {
   *     ExecResult result = ExecHarness.yieldSingle(c ->
   *         Promise.value("foo")
   *           .next(v -> value = v)
   *           .replace(Promise.value("bar"))
   *     );
   *
   *     assertEquals("bar", result.getValue());
   *     assertEquals("foo", value);
   *   }
   * }
   * }
* * @param next the promise to replace {@code this} with * @param the type of the value of the replacement promise * @return a promise * @since 1.1 */ default Promise replace(Promise next) { return flatMap(in -> next); } /** * Transforms the promised value to a {@link Pair}, with the value of the given promise as the {@code left}. *

* The existing promised value will become the {@code right}. * * @param left a promise for the left value of the result pair * @param the type of the left value * @return a promise */ default Promise> left(Promise left) { return flatLeft(t -> left); } /** * Transforms the promised value to a {@link Pair}, with the result of the given function as the {@code left}. *

* The function is called with the promised value. * The existing promised value will become the {@code right}. * * @param leftFunction a function that produces the left value from the promised value * @param the type of the left value * @return a promise * @since 1.4 */ default Promise> left(Function leftFunction) { return map(right -> Pair.of( leftFunction.apply(right), right )); } /** * Transforms the promised value to a {@link Pair}, with the value of the result of the given function as the {@code left}. *

* The function is called with the promised value. * The existing promised value will become the {@code right}. * * @param leftFunction a function that produces a promise for the left value from the promised value * @param the type of the left value * @return a promise * @since 1.4 */ default Promise> flatLeft(Function> leftFunction) { return flatMap(right -> leftFunction.apply(right) .map(left -> Pair.of(left, right) ) ); } /** * Transforms the promised value to a {@link Pair}, with the value of the given promise as the {@code right}. *

* The existing promised value will become the {@code left}. * * @param right a promise for the right value of the result pair * @param the type of the right value * @return a promise */ default Promise> right(Promise right) { return flatRight(t -> right); } /** * Transforms the promised value to a {@link Pair}, with the result of the given function as the {@code right}. *

* The function is called with the promised value. * The existing promised value will become the {@code left}. * * @param rightFunction a function that produces the right value from the promised value * @param the type of the left value * @return a promise * @since 1.4 */ default Promise> right(Function rightFunction) { return map(left -> Pair.of( left, rightFunction.apply(left) )); } /** * Transforms the promised value to a {@link Pair}, with the value of the result of the given function as the {@code right}. *

* The function is called with the promised value. * The existing promised value will become the {@code left}. * * @param rightFunction a function that produces a promise for the right value from the promised value * @param the type of the left value * @return a promise * @since 1.4 */ default Promise> flatRight(Function> rightFunction) { return flatMap(left -> rightFunction.apply(left) .map(right -> Pair.of(left, right) ) ); } default Operation operation() { return operation(Action.noop()); } default Operation operation(Action action) { return new DefaultOperation( map(t -> { action.execute(t); return null; }) ); } /** * Transforms the promise failure (potentially into a value) by applying the given function to it. *

* If the function returns a value, the promise will now be considered successful. *

{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     ExecResult result = ExecHarness.yieldSingle(c ->
   *         Promise.error(new Exception("!"))
   *           .mapError(e -> "value")
   *     );
   *
   *     assertEquals("value", result.getValue());
   *   }
   * }
   * }
*

* If the function throws an exception, that exception will now represent the promise failure. *

{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     ExecResult result = ExecHarness.yieldSingle(c ->
   *         Promise.error(new Exception("!"))
   *           .mapError(e -> { throw new RuntimeException("mapped", e); })
   *     );
   *
   *     assertEquals("mapped", result.getThrowable().getMessage());
   *   }
   * }
   * }
*

* The function will not be called if the promise is successful. * * @param transformer the transformation to apply to the promise failure * @return a promise */ default Promise mapError(Function transformer) { return transform(up -> down -> up.connect(down.onError(throwable -> { try { T transformed = transformer.apply(throwable); down.success(transformed); } catch (Throwable t) { down.error(t); } })) ); } /** * Transforms a failure of the given type (potentially into a value) by applying the given function to it. *

* This method is similar to {@link #mapError(Function)}, except that it will only apply if the error is of the given type. * If the error is not of the given type, it will not be transformed and will propagate as normal. * * @param function the transformation to apply to the promise failure * @return a promise * @since 1.3 */ default Promise mapError(Class type, Function function) { return transform(up -> down -> up.connect(down.onError(throwable -> { if (type.isInstance(throwable)) { T transformed; try { transformed = function.apply(throwable); } catch (Throwable t) { down.error(t); return; } down.success(transformed); } else { down.error(throwable); } })) ); } /** * Transforms a failure of the given type (potentially into a value) by applying the given function to it. *

* This method is similar to {@link #mapError(Function)}, except that it allows async transformation. * * @param function the transformation to apply to the promise failure * @return a promise * @since 1.3 */ default Promise flatMapError(Function> function) { return transform(up -> down -> up.connect(down.onError(throwable -> { Promise transformed; try { transformed = function.apply(throwable); } catch (Throwable t) { down.error(t); return; } transformed.connect(down); })) ); } /** * Transforms a failure of the given type (potentially into a value) by applying the given function to it. *

* This method is similar to {@link #mapError(Class, Function)}, except that it allows async transformation. * * @param function the transformation to apply to the promise failure * @return a promise * @since 1.3 */ default Promise flatMapError(Class type, Function> function) { return transform(up -> down -> up.connect(down.onError(throwable -> { if (type.isInstance(throwable)) { Promise transformed; try { transformed = function.apply(type.cast(throwable)); } catch (Throwable t) { down.error(t); return; } transformed.connect(down); } else { down.error(throwable); } })) ); } /** * Applies the custom operation function to this promise. *

* This method can be used to apply custom operations without breaking the “code flow”. * It works particularly well with method references. *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     Integer value = ExecHarness.yieldSingle(e ->
   *         Promise.value(1)
   *           .apply(Example::dubble)
   *           .apply(Example::triple)
   *     ).getValue();
   *
   *     assertEquals(Integer.valueOf(6), value);
   *   }
   *
   *   public static Promise dubble(Promise input) {
   *     return input.map(i -> i * 2);
   *   }
   *
   *   public static Promise triple(Promise input) {
   *     return input.map(i -> i * 3);
   *   }
   * }
   * }
*

* If the apply function throws an exception, the returned promise will fail. *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     Throwable error = ExecHarness.yieldSingle(e ->
   *         Promise.value(1)
   *           .apply(Example::explode)
   *     ).getThrowable();
   *
   *     assertEquals("bang!", error.getMessage());
   *   }
   *
   *   public static Promise explode(Promise input) throws Exception {
   *     throw new Exception("bang!");
   *   }
   * }
   * }
*

* If the promise having the operation applied to fails, the operation will not be applied. *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     Throwable error = ExecHarness.yieldSingle(e ->
   *         Promise.error(new Exception("bang!"))
   *           .apply(Example::dubble)
   *     ).getThrowable();
   *
   *     assertEquals("bang!", error.getMessage());
   *   }
   *
   *   public static Promise dubble(Promise input) {
   *     return input.map(i -> i * 2);
   *   }
   * }
   * }
* * @param the type of promised object after the operation * @param function the operation implementation * @return the transformed promise */ default Promise apply(Function, ? extends Promise> function) { try { return function.apply(this); } catch (Throwable e) { return Promise.error(e); } } /** * Applies the given function to {@code this} and returns the result. *

* This method can be useful when needing to convert a promise to another type as it facilitates doing so without breaking the “code flow”. * For example, this can be used when integrating with RxJava. *

{@code
   * import ratpack.rx.RxRatpack;
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import java.util.Arrays;
   * import java.util.LinkedList;
   * import java.util.List;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   private static final List LOG = new LinkedList<>();
   *
   *   public static void main(String... args) throws Exception {
   *     ExecHarness.runSingle(e ->
   *         Promise.value("foo")
   *           .to(RxRatpack::observe)
   *           .doOnNext(i -> LOG.add("doOnNext"))
   *           .subscribe(LOG::add)
   *     );
   *
   *     assertEquals(Arrays.asList("doOnNext", "foo"), LOG);
   *   }
   * }
   * }
*

* The given function is executed immediately. *

* This method should only be used when converting a promise to another type. * See {@link #apply(Function)} for applying custom promise operators. * * @param function the promise conversion function * @param the type the promise will be converted to * @return the output of the given function * @throws Exception any thrown by the given function */ default O to(Function, ? extends O> function) throws Exception { return function.apply(this); } /** * Transforms the promised value by applying the given function to it that returns a promise for the transformed value. *

* This is useful when the transformation involves an asynchronous operation. *

{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   * import ratpack.exec.Blocking;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String[] args) throws Exception {
   *     ExecResult result = ExecHarness.yieldSingle(c ->
   *         Promise.value("foo")
   *           .flatMap(s -> Blocking.get(s::toUpperCase))
   *           .map(s -> s + "-BAR")
   *     );
   *
   *     assertEquals("FOO-BAR", result.getValue());
   *   }
   * }
   * }
*

* * @param transformer the transformation to apply to the promised value * @param the type of the transformed object * @return a promise for the transformed value */ default Promise flatMap(Function> transformer) { return transform(up -> down -> up.connect(down.onSuccess(value -> { try { transformer.apply(value).onError(down::error).then(down::success); } catch (Throwable e) { down.error(e); } })) ); } /** * Transforms the promised value by applying the given function to it that returns a promise for the transformed value, if it satisfies the predicate. * *

{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     ExecResult result = ExecHarness.yieldSingle(c ->
   *         Promise.value("foo")
   *           .flatMapIf(s -> s.contains("f"), s -> Promise.value(s.toUpperCase()))
   *           .flatMapIf(s -> s.contains("f"), s -> Promise.value(s + "-BAR"))
   *     );
   *
   *     assertEquals("FOO", result.getValue());
   *   }
   * }
   * }
* * @param predicate the condition to satisfy in order to be transformed * @param transformer the transformation to apply to the promised value * @return a promise * @since 1.4 */ default Promise flatMapIf(Predicate predicate, Function> transformer) { return flatMap(t -> predicate.apply(t) ? transformer.apply(t) : Promise.value(t)); } /** * Allows the promised value to be handled specially if it meets the given predicate, instead of being handled by the promise subscriber. *

* This is typically used for validating values, centrally. *

{@code
   * import com.google.common.collect.Lists;
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   *
   * import java.util.List;
   *
   * import static org.junit.Assert.*;
   *
   * public class Example {
   *   public static ExecResult yield(int i, List collector) throws Exception {
   *     return ExecHarness.yieldSingle(c ->
   *         Promise.value(i)
   *           .route(v -> v > 5, collector::add)
   *     );
   *   }
   *
   *   public static void main(String... args) throws Exception {
   *     List routed = Lists.newLinkedList();
   *
   *     ExecResult result1 = yield(1, routed);
   *     assertEquals(new Integer(1), result1.getValue());
   *     assertFalse(result1.isComplete()); // false because promise returned a value before the execution completed
   *     assertTrue(routed.isEmpty());
   *
   *     ExecResult result10 = yield(10, routed);
   *     assertNull(result10.getValue());
   *     assertTrue(result10.isComplete()); // true because the execution completed before the promised value was returned (i.e. it was routed)
   *     assertTrue(routed.contains(10));
   *   }
   * }
   * }
*

* Be careful about using this where the eventual promise subscriber is unlikely to know that the promise * will routed as it can be surprising when neither the promised value nor an error appears. *

* It can be useful at the handler layer to provide common validation. *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.handling.Context;
   * import ratpack.test.embed.EmbeddedApp;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static Promise getAge(Context ctx) {
   *     return Promise.value(10)
   *       .route(
   *         i -> i < 21,
   *         i -> ctx.render(i + " is too young to be here!")
   *       );
   *   }
   *
   *   public static void main(String... args) throws Exception {
   *     EmbeddedApp.fromHandler(ctx ->
   *         getAge(ctx).then(age -> ctx.render("welcome!"))
   *     ).test(httpClient -> {
   *       assertEquals("10 is too young to be here!", httpClient.getText());
   *     });
   *   }
   * }
   * }
*

* If the routed-to action throws an exception, it will be forwarded down the promise chain. * * @param predicate the condition under which the value should be routed * @param action the terminal action for the value * @return a routed promise */ default Promise route(Predicate predicate, Action action) { return transform(up -> down -> up.connect(down.onSuccess(value -> { boolean apply; try { apply = predicate.apply(value); } catch (Throwable e) { down.error(e); return; } if (apply) { try { action.execute(value); down.complete(); } catch (Throwable e) { down.error(e); } } else { down.success(value); } })) ); } /** * A convenience shorthand for {@link #route(Predicate, Action) routing} {@code null} values. *

* If the promised value is {@code null}, the given action will be called. * * @param action the action to route to if the promised value is null * @return a routed promise */ default Promise onNull(Block action) { return route(Objects::isNull, ignoreArg(action)); } /** * Caches the promised value (or error) and returns it to all subscribers. *

* This method is equivalent to using {@link #cacheResultIf(Predicate)} with a predicate that always returns {@code true}. * *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import java.util.concurrent.atomic.AtomicLong;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     ExecHarness.runSingle(c -> {
   *       AtomicLong counter = new AtomicLong();
   *       Promise uncached = Promise.async(f -> f.success(counter.getAndIncrement()));
   *
   *       uncached.then(i -> assertEquals(0l, i.longValue()));
   *       uncached.then(i -> assertEquals(1l, i.longValue()));
   *       uncached.then(i -> assertEquals(2l, i.longValue()));
   *
   *       Promise cached = uncached.cache();
   *
   *       cached.then(i -> assertEquals(3l, i.longValue()));
   *       cached.then(i -> assertEquals(3l, i.longValue()));
   *
   *       uncached.then(i -> assertEquals(4l, i.longValue()));
   *       cached.then(i -> assertEquals(3l, i.longValue()));
   *     });
   *   }
   * }
   * }
* *

* If the cached promise fails, the same exception will be returned every time. * *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import static org.junit.Assert.assertTrue;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     ExecHarness.runSingle(c -> {
   *       Throwable error = new Exception("bang!");
   *       Promise cached = Promise.error(error).cache();
   *       cached.onError(t -> assertTrue(t == error)).then(i -> assertTrue("not called", false));
   *       cached.onError(t -> assertTrue(t == error)).then(i -> assertTrue("not called", false));
   *       cached.onError(t -> assertTrue(t == error)).then(i -> assertTrue("not called", false));
   *     });
   *   }
   * }
   * }
   *
   * @return a caching promise
   * @see #cacheIf(Predicate)
   * @see #cacheResultIf(Predicate)
   */
  default Promise cache() {
    return cacheResultIf(Predicate.TRUE);
  }

  /**
   * Caches the promise value and provides it to all future subscribers, if it satisfies the predicate.
   * 

* This method is equivalent to using {@link #cacheResultIf(Predicate)} with a predicate that requires * a successful result and for the value to satisfy the predicate given to this method. *

* Non success results will not be cached. * * @param shouldCache the test for whether a successful result is cacheable * @return a caching promise * @since 1.4 */ default Promise cacheIf(Predicate shouldCache) { return cacheResultIf(r -> r.isSuccess() && shouldCache.apply(r.getValue())); } /** * Caches the promise result and provides it to all future subscribers, if it satisfies the predicate. *

* This method is typically used when wanting to cache a failure result or a success result. * Moreover, the error throwable or success value can be inspected to determine whether it should be cached. *

* A cached promise is fully threadsafe and and can be subscribed to concurrently. * While there is no cached value, yielding the upstream value is serialised. * That is, one value is requested at a time regardless of concurrent subscription. * If a cache-able value is received, all pending subscribers will received the cache-able value. * If a received value is not cache-able the corresponding subscriber will receive the value, * and the upstream promise will be subscribed to again on behalf of the next subscriber. * *

{@code
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import java.util.ArrayList;
   * import java.util.List;
   * import java.util.concurrent.atomic.AtomicInteger;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *
   *   public static void main(String... args) throws Exception {
   *     List> results = new ArrayList<>();
   *     AtomicInteger counter = new AtomicInteger();
   *     Promise promise = Promise.sync(() -> {
   *       int i = counter.getAndIncrement();
   *       if (i < 2) {
   *         return i;
   *       } else if (i == 2) {
   *         throw new Exception(Integer.toString(i));
   *       } else if (i == 3) {
   *         throw new RuntimeException(Integer.toString(i));
   *       } else {
   *         throw new IllegalStateException(Integer.toString(i));
   *       }
   *     });
   *
   *     Promise cachedPromise = promise.cacheResultIf(r ->
   *       (r.isError() && r.getThrowable().getClass() == RuntimeException.class)
   *         || (r.isSuccess() && r.getValue() > 10)
   *     );
   *
   *     ExecHarness.runSingle(e -> {
   *       for (int i = 0; i < 6; i++) {
   *         cachedPromise.result(results::add);
   *       }
   *     });
   *
   *     assertEquals(results.get(0).getValueOrThrow(), Integer.valueOf(0));
   *     assertEquals(results.get(1).getValueOrThrow(), Integer.valueOf(1));
   *     assertEquals(results.get(2).getThrowable().getClass(), Exception.class);
   *     assertEquals(results.get(3).getThrowable().getClass(), RuntimeException.class);
   *
   *     // value is now cached
   *     assertEquals(results.get(4).getThrowable().getClass(), RuntimeException.class);
   *     assertEquals(results.get(5).getThrowable().getClass(), RuntimeException.class);
   *   }
   * }
   * }
* *

* Note, the cached value never expires. * If you wish to cache a value only for a certain amount of time, * use a general caching tool such as Guava's {@link LoadingCache}. * *

{@code
   * import com.google.common.cache.CacheBuilder;
   * import com.google.common.cache.CacheLoader;
   * import com.google.common.cache.LoadingCache;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * import java.util.ArrayList;
   * import java.util.List;
   * import java.util.concurrent.TimeUnit;
   * import java.util.concurrent.atomic.AtomicInteger;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *
   *   public static void main(String... args) throws Exception {
   *     List> results = new ArrayList<>();
   *     AtomicInteger counter = new AtomicInteger();
   *     Promise promise = Promise.sync(counter::getAndIncrement);
   *
   *     LoadingCache> cache = CacheBuilder.newBuilder()
   *       .expireAfterWrite(2, TimeUnit.SECONDS)
   *       .build(new CacheLoader>() {
   *         public Promise load(String key) throws Exception {
   *           return promise.cacheResultIf(i -> i.isSuccess() && i.getValue() > 1);
   *         }
   *       });
   *
   *     ExecHarness.runSingle(e -> {
   *       for (int i = 0; i < 4; i++) {
   *         cache.get("key").result(results::add);
   *       }
   *
   *       // let the cache entry expire
   *       Thread.sleep(2000);
   *
   *       for (int i = 0; i < 2; i++) {
   *         cache.get("key").result(results::add);
   *       }
   *     });
   *
   *     assertEquals(results.get(0).getValueOrThrow(), Integer.valueOf(0));
   *     assertEquals(results.get(1).getValueOrThrow(), Integer.valueOf(1));
   *     assertEquals(results.get(2).getValueOrThrow(), Integer.valueOf(2));
   *     assertEquals(results.get(3).getValueOrThrow(), Integer.valueOf(2));
   *
   *     // cache entry has expired
   *
   *     assertEquals(results.get(4).getValueOrThrow(), Integer.valueOf(3));
   *     assertEquals(results.get(5).getValueOrThrow(), Integer.valueOf(3));
   *   }
   * }
   * }
* * @param shouldCache the test for whether a result is cacheable * @return a caching promise * @since 1.4 */ default Promise cacheResultIf(Predicate> shouldCache) { return transform(up -> new CachingUpstream<>(up, shouldCache)); } /** * Allows the execution of the promise to be deferred to a later time. *

* When the returned promise is subscribed to, the given {@code releaser} action will be invoked. * The execution of {@code this} promise is deferred until the runnable given to the {@code releaser} is run. *

* It is generally more convenient to use {@link #throttled(Throttle)} or {@link #onYield(Runnable)} than this operation. * * @param releaser the action that will initiate the execution some time later * @return a deferred promise */ default Promise defer(Action releaser) { return transform(up -> down -> Promise.async(innerDown -> releaser.execute((Runnable) () -> innerDown.success(true)) ).then(v -> up.connect(down) ) ); } /** * Registers a listener that is invoked when {@code this} promise is initiated. *

{@code
   * import com.google.common.collect.Lists;
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.Promise;
   *
   * import java.util.Arrays;
   * import java.util.List;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     List events = Lists.newLinkedList();
   *     ExecHarness.runSingle(c ->
   *         Promise.sync(() -> {
   *           events.add("promise");
   *           return "foo";
   *         })
   *           .onYield(() -> events.add("onYield"))
   *           .then(v -> events.add("then"))
   *     );
   *     assertEquals(Arrays.asList("onYield", "promise", "then"), events);
   *   }
   * }
   * }
* * @param onYield the action to take when the promise is initiated * @return effectively, {@code this} promise */ default Promise onYield(Runnable onYield) { return transform(up -> down -> { try { onYield.run(); } catch (Throwable e) { down.error(e); return; } up.connect(down); }); } /** * Registers a listener for the promise outcome. *
{@code
   * import com.google.common.collect.Lists;
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.Promise;
   *
   * import java.util.Arrays;
   * import java.util.List;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     List events = Lists.newLinkedList();
   *     ExecHarness.runSingle(c ->
   *         Promise.sync(() -> {
   *           events.add("promise");
   *           return "foo";
   *         })
   *           .wiretap(r -> events.add("wiretap: " + r.getValue()))
   *           .then(v -> events.add("then"))
   *     );
   *
   *     assertEquals(Arrays.asList("promise", "wiretap: foo", "then"), events);
   *   }
   * }
   * }
* * @param listener the result listener * @return effectively, {@code this} promise */ default Promise wiretap(Action> listener) { return transform(up -> down -> up.connect(new Downstream() { @Override public void success(T value) { try { listener.execute(Result.success(value)); } catch (Exception e) { down.error(e); return; } down.success(value); } @Override public void error(Throwable throwable) { try { listener.execute(Result.error(throwable)); } catch (Exception e) { throwable.addSuppressed(e); } down.error(throwable); } @Override public void complete() { down.complete(); } }) ); } /** * Throttles {@code this} promise, using the given {@link Throttle throttle}. *

* Throttling can be used to limit concurrency. * Typically to limit concurrent use of an external resource, such as a HTTP API. *

* Note that the {@link Throttle} instance given defines the actual throttling semantics. *

{@code
   * import ratpack.exec.Throttle;
   * import ratpack.exec.Promise;
   * import ratpack.exec.Execution;
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   *
   * import java.util.concurrent.atomic.AtomicInteger;
   *
   * import static org.junit.Assert.assertTrue;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     int numJobs = 1000;
   *     int maxAtOnce = 10;
   *
   *     ExecResult result = ExecHarness.yieldSingle(exec -> {
   *       AtomicInteger maxConcurrent = new AtomicInteger();
   *       AtomicInteger active = new AtomicInteger();
   *       AtomicInteger done = new AtomicInteger();
   *
   *       Throttle throttle = Throttle.ofSize(maxAtOnce);
   *
   *       // Launch numJobs forked executions, and return the maximum number that were executing at any given time
   *       return Promise.async(downstream -> {
   *         for (int i = 0; i < numJobs; i++) {
   *           Execution.fork().start(forkedExec ->
   *             Promise.sync(() -> {
   *               int activeNow = active.incrementAndGet();
   *               int maxConcurrentVal = maxConcurrent.updateAndGet(m -> Math.max(m, activeNow));
   *               active.decrementAndGet();
   *               return maxConcurrentVal;
   *             })
   *             .throttled(throttle) // limit concurrency
   *             .then(max -> {
   *               if (done.incrementAndGet() == numJobs) {
   *                 downstream.success(max);
   *               }
   *             })
   *           );
   *         }
   *       });
   *     });
   *
   *     assertTrue(result.getValue() <= maxAtOnce);
   *   }
   * }
   * }
* * @param throttle the particular throttle to use to throttle the operation * @return the throttled promise */ default Promise throttled(Throttle throttle) { return throttle.throttle(this); } /** * Closes the given closeable when the value or error propagates to this point. *

* This can be used to simulate a try/finally synchronous construct. * It is typically used to close some resource after an asynchronous operation. * *

{@code
   * import org.junit.Assert;
   * import ratpack.exec.Promise;
   * import ratpack.test.exec.ExecHarness;
   *
   * public class Example {
   *   static class MyResource implements AutoCloseable {
   *     final boolean inError;
   *     boolean closed;
   *
   *     public MyResource(boolean inError) {
   *       this.inError = inError;
   *     }
   *
   *     {@literal @}Override
   *     public void close() {
   *       closed = true;
   *     }
   *   }
   *
   *   static Promise resourceUsingMethod(MyResource resource) {
   *     return Promise.sync(() -> {
   *       if (resource.inError) {
   *         throw new Exception("error!");
   *       } else {
   *         return "ok!";
   *       }
   *     });
   *   }
   *
   *   public static void main(String[] args) throws Exception {
   *     ExecHarness.runSingle(e -> {
   *       MyResource myResource = new MyResource(false);
   *       resourceUsingMethod(myResource)
   *         .close(myResource)
   *         .then(value -> Assert.assertTrue(myResource.closed));
   *     });
   *
   *     ExecHarness.runSingle(e -> {
   *       MyResource myResource = new MyResource(true);
   *       resourceUsingMethod(myResource)
   *         .close(myResource)
   *         .onError(error -> Assert.assertTrue(myResource.closed))
   *         .then(value -> {
   *           throw new UnsupportedOperationException("should not reach here!");
   *         });
   *     });
   *
   *   }
   * }
   * }
*

* The general pattern is to open the resource, and then pass it to some method/closure that works with it and returns a promise. * This method is then called on the returned promise to cleanup the resource. * * @param closeable the closeable to close * @since 1.3 */ default Promise close(AutoCloseable closeable) { return transform(up -> down -> up.connect(new Downstream() { @Override public void success(T value) { try { closeable.close(); } catch (Exception e) { down.error(e); return; } down.success(value); } @Override public void error(Throwable throwable) { try { closeable.close(); } catch (Exception e) { throwable.addSuppressed(e); } down.error(throwable); } @Override public void complete() { try { closeable.close(); } catch (Exception e) { down.error(e); return; } down.complete(); } }) ); } /** * Emits the time taken from when the promise is subscribed to to when the result is available. *

* The given {@code action} is called regardless of whether the promise is successful or not. *

* If the promise fails and this method throws an exception, the original exception will propagate with the thrown exception suppressed. * If the promise succeeds and this method throws an exception, the thrown exception will propagate. * * @param action a callback for the time * @since 1.3 * @return effectively {@code this} */ default Promise time(Action action) { return transform(up -> down -> { long start = System.nanoTime(); up.connect(new Downstream() { private Duration duration() { // protect against clock skew causing negative durations return Duration.ofNanos(Math.max(0, System.nanoTime() - start)); } @Override public void success(T value) { try { action.execute(duration()); } catch (Throwable t) { down.error(t); return; } down.success(value); } @Override public void error(Throwable throwable) { try { action.execute(duration()); } catch (Throwable t) { throwable.addSuppressed(t); } down.error(throwable); } @Override public void complete() { try { action.execute(duration()); } catch (Throwable t) { down.error(t); return; } down.complete(); } }); }); } /** * Forks a new execution and subscribes to this promise, returning a promise for its value. *

* The new execution is created and started immediately by this method, effectively subscribing to the promise immediately. * The returned promise provides the value when the execution completes. *

* This method can be used for simple of processing. * It is often combined with the {@link #left(Promise)} or {@link #right(Promise)}. * *

{@code
   * import ratpack.exec.Blocking;
   * import ratpack.exec.Promise;
   * import ratpack.func.Pair;
   * import ratpack.test.exec.ExecHarness;
   *
   * import java.util.concurrent.CyclicBarrier;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *
   *   public static void main(String... args) throws Exception {
   *     CyclicBarrier barrier = new CyclicBarrier(2);
   *
   *     Pair result = ExecHarness.yieldSingle(r -> {
   *       Promise p1 = Blocking.get(() -> {
   *         barrier.await();
   *         return 1;
   *       });
   *       Promise p2 = Blocking.get(() -> {
   *         barrier.await();
   *         return "2";
   *       });
   *
   *       return p1.right(p2.fork());
   *     }).getValueOrThrow();
   *
   *     assertEquals(result, Pair.of(1, "2"));
   *   }
   *
   * }
   * }
* * @param execSpec configuration for the forked execution * @return a promise * @throws Exception any thrown by {@code execSpec} * @since 1.4 */ default Promise fork(Action execSpec) throws Exception { Promised promised = new Promised<>(); execSpec.with(Execution.fork()).start(e -> connect(promised)); return promised.promise(); } /** * Forks a new execution and subscribes to this promise, returning a promise for its value. *

* This method delegates to {@link #fork(Action)} with {@link Action#noop()}. * * @return a promise * @since 1.4 * @see #fork(Action) */ default Promise fork() { return Exceptions.uncheck(() -> fork(Action.noop())); } static Promise wrap(Factory> factory) { try { return factory.create(); } catch (Exception e) { return Promise.error(e); } } }