Interface Promise<T>
-
- Type Parameters:
T
- the type of promised value
public interface Promise<T>
A promise for a single value.A promise is a representation of a value which will become available later. Methods such as
map(Function)
,flatMap(Function)
,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 thetransform(Function)
method. Each operation returns a new promise object, not the original promise object.To create a promise, use the
async(Upstream)
method (or one of the variants such assync(Factory)
. To test code that uses promises, use theExecHarness
.The promise is not “activated” until the
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
then(Action)
) invokes the function. Thecache()
operation can be used to change this behaviour.
-
-
Method Summary
All Methods Static Methods Instance Methods Abstract Methods Default Methods Modifier and Type Method Description default <O> Promise<O>
apply(Function<? super Promise<T>,? extends Promise<O>> function)
Applies the custom operation function to this promise.default <B,A>
Promise<A>around(Factory<? extends B> before, BiFunction<? super B,? super ExecResult<T>,? extends ExecResult<A>> after)
Facilitates capturing a value before the the promise is subscribed and using it to later augment the result.static <T> Promise<T>
async(Upstream<T> upstream)
Creates a promise for value that will be produced asynchronously.default <O> Promise<O>
blockingMap(Function<? super T,? extends O> transformer)
Likemap(Function)
, but performs the transformation on a blocking thread.default Promise<T>
blockingOp(Action<? super T> action)
Executes the given action with the promise value, on a blocking thread.default Promise<T>
cache()
Caches the promised value (or error) and returns it to all subscribers.default Promise<T>
cacheIf(Predicate<? super T> shouldCache)
Caches the promise value and provides it to all future subscribers, if it satisfies the predicate.default Promise<T>
cacheResultFor(Function<? super ExecResult<T>,Duration> cacheFor)
Caches the promise result for a calculated amount of time.default Promise<T>
cacheResultIf(Predicate<? super ExecResult<T>> shouldCache)
Caches the promise result eternally and provide it to all future subscribers, if it satisfies the predicate.default Promise<T>
close(AutoCloseable closeable)
Closes the given closeable when the value or error propagates to this point.default Promise<T>
close(Operation closer)
Likeclose(AutoCloseable)
, but allows async close operations.void
connect(Downstream<? super T> downstream)
A low level hook for consuming the promised value.default Promise<T>
defer(Duration duration)
Defers the subscription ofthis
promise for the given duration.default Promise<T>
defer(Action<? super Runnable> releaser)
Defers the subscription ofthis
promise until later.static <T> Promise<T>
error(Throwable t)
Creates a failed promise with the given error.default <O> Promise<Pair<O,T>>
flatLeft(Function<? super T,? extends Promise<O>> leftFunction)
Transforms the promised value to aPair
, with the value of the result of the given function as theleft
.default <O> Promise<O>
flatMap(Function<? super T,? extends Promise<O>> transformer)
Transforms the promised value by applying the given function to it that returns a promise for the transformed value.default <E extends Throwable>
Promise<T>flatMapError(Class<E> type, Function<? super E,? extends Promise<T>> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.default Promise<T>
flatMapError(Function<? super Throwable,? extends Promise<T>> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.default Promise<T>
flatMapError(Predicate<? super Throwable> predicate, Function<? super Throwable,? extends Promise<T>> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.default <O> Promise<O>
flatMapIf(Predicate<? super T> predicate, Function<? super T,? extends Promise<O>> onTrue, Function<? super T,? extends Promise<O>> onFalse)
Transforms the promised value by applying one of the given functions to it that returns a promise for the transformed value, depending if it satisfies the predicate.default Promise<T>
flatMapIf(Predicate<? super T> predicate, Function<? super T,? extends Promise<T>> transformer)
Transforms the promised value by applying the given function to it that returns a promise for the transformed value, if it satisfies the predicate.default Operation
flatOp(Function<? super T,? extends Operation> function)
Converts this promise to an operation, which is the return offunction
.default <O> Promise<Pair<T,O>>
flatRight(Function<? super T,? extends Promise<O>> rightFunction)
Transforms the promised value to aPair
, with the value of the result of the given function as theright
.static <T> Promise<T>
flatten(Factory<? extends Promise<T>> factory)
Creates a promise for the promise produced by the given factory.default Promise<T>
fork()
Forks a new execution and subscribes to this promise, returning a promise for its value.default Promise<T>
fork(Action<? super ExecSpec> execSpec)
Forks a new execution and subscribes to this promise, returning a promise for its value.default <O> Promise<Pair<O,T>>
left(Promise<O> left)
Transforms the promised value to aPair
, with the value of the given promise as theleft
.default <O> Promise<Pair<O,T>>
left(Function<? super T,? extends O> leftFunction)
Transforms the promised value to aPair
, with the result of the given function as theleft
.default <O> Promise<O>
map(Function<? super T,? extends O> transformer)
Transforms the promised value by applying the given function to it.default <E extends Throwable>
Promise<T>mapError(Class<E> type, Function<? super E,? extends T> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.default Promise<T>
mapError(Function<? super Throwable,? extends T> transformer)
Transforms the promise failure (potentially into a value) by applying the given function to it.default Promise<T>
mapError(Predicate<? super Throwable> predicate, Function<? super Throwable,? extends T> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.default <O> Promise<O>
mapIf(Predicate<? super T> predicate, Function<? super T,? extends O> onTrue, Function<? super T,? extends O> onFalse)
Transforms the promised value by applying one of the given functions to it, depending if it satisfies the predicate.default Promise<T>
mapIf(Predicate<? super T> predicate, Function<? super T,? extends T> transformer)
Transforms the promised value by applying the given function to it, if it satisfies the predicate.default Promise<T>
next(Action<? super T> action)
Executes the provided, potentially asynchronous,Action
with the promised value as input.default Promise<T>
nextOp(Function<? super T,? extends Operation> function)
Executes the operation returned by the given function.default Promise<T>
nextOpIf(Predicate<? super T> predicate, Function<? super T,? extends Operation> function)
Executes the operation returned by the given function, if it satisfies the predicate.static <T> Promise<T>
ofNull()
A promise fornull
.default Promise<T>
onComplete(Block block)
Specifies the action to take if theUpstream
signals complete without emitting a value or an error.default <E extends Throwable>
Promise<T>onError(Class<E> errorType, Action<? super E> errorHandler)
Specifies the action to take if the an error of the given type occurs trying to produce the promised value.default Promise<T>
onError(Action<? super Throwable> errorHandler)
Specifies the action to take if an error occurs trying to produce the promised value.default Promise<T>
onError(Predicate<? super Throwable> predicate, Action<? super Throwable> errorHandler)
Specifies the action to take if the an error occurs trying to produce the promised value, that the given predicate applies to.default Promise<T>
onNull(Block action)
A convenience shorthand forrouting
null
values.default Promise<T>
onYield(Runnable onYield)
Registers a listener that is invoked whenthis
promise is initiated.default Operation
operation()
Converts this promise to an operation, by effectively discarding the result.default Operation
operation(Action<? super T> action)
Converts this promise to an operation which is effectivelyaction
.default <O> Promise<O>
replace(Promise<O> next)
Replacesthis
promise with the provided promise for downstream subscribers.default void
result(Action<? super ExecResult<T>> resultHandler)
Consume the promised value as aResult
.default Promise<T>
retry(RetryPolicy retryPolicy, BiAction<? super Integer,? super Throwable> onError)
Causesthis
yielding the promised value to be retried on error, under the rules of providedretryPolicy
.default Promise<T>
retryIf(Predicate<? super Throwable> predicate, RetryPolicy retryPolicy, BiAction<? super Integer,? super Throwable> onError)
Causesthis
yielding the promised value to be retried on error, under the rules of providedretryPolicy
, and if the givenPredicate
matches the error thrown.default <O> Promise<Pair<T,O>>
right(Promise<O> right)
Transforms the promised value to aPair
, with the value of the given promise as theright
.default <O> Promise<Pair<T,O>>
right(Function<? super T,? extends O> rightFunction)
Transforms the promised value to aPair
, with the result of the given function as theright
.default Promise<T>
route(Predicate<? super T> predicate, Action<? super T> action)
Allows the promised value to be handled specially if it meets the given predicate, instead of being handled by the promise subscriber.static <T> Promise<T>
sync(Factory<T> factory)
Creates a promise for the value synchronously produced by the given factory.void
then(Action<? super T> then)
Specifies what should be done with the promised object when it becomes available.default Promise<T>
throttled(Throttle throttle)
Throttlesthis
promise, using the giventhrottle
.default Promise<T>
time(Action<? super Duration> action)
Emits the time taken from when the promise is subscribed to to when the result is available.default Promise<Pair<ExecResult<T>,Duration>>
timeResult()
default Promise<T>
timeResult(BiAction<? super ExecResult<T>,? super Duration> action)
Emits the time taken from when the promise is subscribed to to when the result is available.default <O> O
to(Function<? super Promise<T>,? extends O> function)
Applies the given function tothis
and returns the result.default CompletableFuture<T>
toCompletableFuture()
Convert this promise into aCompletableFuture
.static <T> Promise<T>
toPromise(CompletableFuture<T> future)
Convert aCompletableFuture
into a promise.<O> Promise<O>
transform(Function<? super Upstream<? extends T>,? extends Upstream<O>> upstreamTransformer)
Apply a custom transform to this promise.static <T> Promise<T>
value(T t)
Creates a promise for the given item.default Promise<T>
wiretap(Action<? super Result<T>> listener)
Registers a listener for the promise outcome.
-
-
-
Method Detail
-
async
static <T> Promise<T> async(Upstream<T> upstream)
Creates a promise for value that will be produced asynchronously.The
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.import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String[] args) throws Exception { String value = ExecHarness.yieldSingle(e -> Promise.<String>async(down -> new Thread(() -> { down.success("foo"); }).start() ) ).getValueOrThrow(); assertEquals(value, "foo"); } }
- Type Parameters:
T
- the type of promised value- Parameters:
upstream
- the producer of the value- Returns:
- a promise for the asynchronously created value
- Since:
- 1.3
- See Also:
Upstream
,sync(Factory)
,value(Object)
,error(Throwable)
-
sync
static <T> Promise<T> sync(Factory<T> factory)
Creates a promise for the 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.
import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import static org.junit.jupiter.api.Assertions.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.
- Type Parameters:
T
- the type of promised value- Parameters:
factory
- the producer of the value- Returns:
- a promise for the result of the factory
- Since:
- 1.3
- See Also:
async(Upstream)
,value(Object)
,error(Throwable)
-
flatten
static <T> Promise<T> flatten(Factory<? extends Promise<T>> factory)
Creates a promise for the promise 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.
This can be used to effectively prepend work to another promise.
- Type Parameters:
T
- the type of promised value- Parameters:
factory
- the producer of the promise to return- Returns:
- a promise for the result of the factory
- Since:
- 1.5
-
value
static <T> Promise<T> value(T t)
Creates a promise for the given item.The given item will be used every time that the value is requested.
import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String[] args) throws Exception { String value = ExecHarness.yieldSingle(e -> Promise.value("foo") ).getValueOrThrow(); assertEquals(value, "foo"); } }
- Type Parameters:
T
- the type of promised value- Parameters:
t
- the promised value- Returns:
- a promise for the given item
- See Also:
async(Upstream)
,sync(Factory)
,error(Throwable)
-
ofNull
static <T> Promise<T> ofNull()
A promise fornull
.- Type Parameters:
T
- the type of promised value- Returns:
- a promise for
null
. - Since:
- 1.5
-
error
static <T> Promise<T> error(Throwable t)
Creates a failed promise with the given error.The given error will be used every time that the value is requested.
import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import static org.junit.jupiter.api.Assertions.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); } }
- Type Parameters:
T
- the type of promised value- Parameters:
t
- the error- Returns:
- a failed promise
- See Also:
async(Upstream)
,sync(Factory)
,value(Object)
-
then
void then(Action<? super T> then)
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
ExecutionException
.- Parameters:
then
- the receiver of the promised value- Throws:
ExecutionException
- if not called on a Ratpack managed compute thread
-
connect
void connect(Downstream<? super T> downstream)
A low level hook for consuming the promised value.It is generally preferable to use
then(Action)
over this method.- Parameters:
downstream
- the downstream consumer
-
transform
<O> Promise<O> transform(Function<? super Upstream<? extends T>,? extends Upstream<O>> upstreamTransformer)
Apply a custom transform to this promise.This method is the basis for the standard operations of this interface, such as
map(Function)
. The following is a non generic implementation of a map that converts the value to upper case.import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { ExecResult<String> result = ExecHarness.yieldSingle(c -> Promise.value("foo") .transform(up -> down -> up.connect(down.<String>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
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.
- Type Parameters:
O
- the type of item emitted by the transformed upstream- Parameters:
upstreamTransformer
- a function that returns a new upstream, typically wrapping the given upstream argument- Returns:
- a new promise
-
onError
default Promise<T> onError(Predicate<? super Throwable> predicate, @NonBlocking Action<? super Throwable> errorHandler)
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.
- Parameters:
predicate
- the predicate to test against the errorerrorHandler
- the action to take if an error occurs- Returns:
- A promise for the successful result
- Since:
- 1.1
-
onError
default <E extends Throwable> Promise<T> onError(Class<E> errorType, Action<? super E> errorHandler)
Specifies the action to take if the an error of the given type occurs trying to produce the promised value.import ratpack.core.http.TypedData; import ratpack.test.embed.EmbeddedApp; import static org.junit.jupiter.api.Assertions.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.
- Type Parameters:
E
- the type of exception to handle with the given action- Parameters:
errorType
- the type of exception to handle with the given actionerrorHandler
- the action to take if an error occurs- Returns:
- A promise for the successful result
- Since:
- 1.1
-
onError
default Promise<T> onError(Action<? super Throwable> errorHandler)
Specifies the action to take if 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.
- Parameters:
errorHandler
- the action to take if an error occurs- Returns:
- A promise for the successful result
-
result
default void result(Action<? super ExecResult<T>> resultHandler)
Consume the promised value as aResult
.This method is an alternative to
then(Action)
andonError(Action)
.- Parameters:
resultHandler
- the consumer of the result
-
map
default <O> Promise<O> map(Function<? super T,? extends O> transformer)
Transforms the promised value by applying the given function to it.import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { ExecResult<String> result = ExecHarness.yieldSingle(c -> Promise.value("foo") .map(String::toUpperCase) .map(s -> s + "-BAR") ); assertEquals("FOO-BAR", result.getValue()); } }
- Type Parameters:
O
- the type of the transformed object- Parameters:
transformer
- the transformation to apply to the promised value- Returns:
- a promise for the transformed value
-
mapIf
default Promise<T> mapIf(Predicate<? super T> predicate, Function<? super T,? extends T> transformer)
Transforms the promised value by applying the given function to it, if it satisfies the predicate.import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { ExecResult<String> 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()); } }
- Parameters:
predicate
- the condition to satisfy in order to be transformedtransformer
- the transformation to apply to the promised value- Returns:
- a promise
- Since:
- 1.4
-
mapIf
default <O> Promise<O> mapIf(Predicate<? super T> predicate, Function<? super T,? extends O> onTrue, Function<? super T,? extends O> onFalse)
Transforms the promised value by applying one of the given functions to it, depending if it satisfies the predicate.import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { ExecResult<String> result = ExecHarness.yieldSingle(c -> Promise.value("foo") .mapIf(s -> s.contains("f"), String::toUpperCase, s -> s) .mapIf(s -> s.contains("f"), s -> s, s -> s + "-BAR") ); assertEquals("FOO-BAR", result.getValue()); } }
- Type Parameters:
O
- the type of the transformed object- Parameters:
predicate
- the condition to decide which transformation to applyonTrue
- the transformation to apply when the predicate is trueonFalse
- the transformation to apply when the predicate is false- Returns:
- a promise
- Since:
- 1.5
-
blockingMap
default <O> Promise<O> blockingMap(Function<? super T,? extends O> transformer)
Likemap(Function)
, but performs the transformation on a blocking thread.This is simply a more convenient form of using
Blocking.get(Factory)
andflatMap(Function)
.- Type Parameters:
O
- the type of the transformed object- Parameters:
transformer
- the transformation to apply to the promised value, on a blocking thread- Returns:
- a promise for the transformed value
-
blockingOp
default Promise<T> blockingOp(Action<? super T> action)
Executes the given action with the promise value, on a blocking thread.Similar to
blockingMap(Function)
, but does not provide a new value. This can be used to do something with the value, without terminating the promise.- Parameters:
action
- the action to to perform with the value, on a blocking thread- Returns:
- a promise for the same value given to the action
-
next
default Promise<T> next(@NonBlocking Action<? super T> action)
Executes the provided, potentially asynchronous,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
Operation
, allowing it to perform asynchronous work.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.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { List<String> 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); } }
- Parameters:
action
- the action to execute with the promised value- Returns:
- a promise for the original value
- Since:
- 1.1
- See Also:
nextOp(Function)
-
nextOp
default Promise<T> nextOp(Function<? super T,? extends Operation> function)
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.
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.jupiter.api.Assertions.assertEquals; public class Example { public static class CaseService { public Operation toUpper(String value, List<String> values) { return Operation.of(() -> values.add(value.toUpperCase())); } } public static void main(String... args) throws Exception { CaseService service = new CaseService(); List<String> events = Lists.newLinkedList(); ExecHarness.runSingle(c -> Promise.value("foo") .nextOp(v -> service.toUpper(v, events)) .then(events::add) ); assertEquals(Arrays.asList("FOO", "foo"), events); } }
- Parameters:
function
- a function that returns an operation that acts on the promised value- Returns:
- a promise for the original value
- Since:
- 1.1
- See Also:
next(Action)
-
nextOpIf
default Promise<T> nextOpIf(Predicate<? super T> predicate, Function<? super T,? extends Operation> function)
Executes the operation returned by the given function, if it satisfies the predicate.This method can be used when needing to perform an operation returned by another object, based on the promised value.
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.jupiter.api.Assertions.assertEquals; public class Example { public static class CaseService { public Operation toUpper(String value, List<String> values) { return Operation.of(() -> values.add(value.toUpperCase())); } } public static void main(String... args) throws Exception { CaseService service = new CaseService(); List<String> events = Lists.newLinkedList(); ExecHarness.runSingle(c -> Promise.value("foo") .nextOpIf(v -> v.startsWith("f"), v -> service.toUpper(v, events)) .nextOpIf(v -> v.isEmpty(), v -> Operation.of(() -> events.add("empty"))) .then(events::add) ); assertEquals(Arrays.asList("FOO", "foo"), events); } }
- Parameters:
predicate
- the condition to satisfy in order to execute the operation.function
- a function that returns an operation that acts on the promised value- Returns:
- a promise for the original value
- Since:
- 1.5
-
replace
default <O> Promise<O> replace(Promise<O> next)
Replacesthis
promise with the provided promise for downstream subscribers.This is simply a more convenient form of
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.
import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { private static String value; public static void main(String... args) throws Exception { ExecResult<String> result = ExecHarness.yieldSingle(c -> Promise.value("foo") .next(v -> value = v) .replace(Promise.value("bar")) ); assertEquals("bar", result.getValue()); assertEquals("foo", value); } }
- Type Parameters:
O
- the type of the value of the replacement promise- Parameters:
next
- the promise to replacethis
with- Returns:
- a promise
- Since:
- 1.1
-
left
default <O> Promise<Pair<O,T>> left(Promise<O> left)
Transforms the promised value to aPair
, with the value of the given promise as theleft
.The existing promised value will become the
right
.- Type Parameters:
O
- the type of the left value- Parameters:
left
- a promise for the left value of the result pair- Returns:
- a promise
-
left
default <O> Promise<Pair<O,T>> left(Function<? super T,? extends O> leftFunction)
Transforms the promised value to aPair
, with the result of the given function as theleft
.The function is called with the promised value. The existing promised value will become the
right
.- Type Parameters:
O
- the type of the left value- Parameters:
leftFunction
- a function that produces the left value from the promised value- Returns:
- a promise
- Since:
- 1.4
-
flatLeft
default <O> Promise<Pair<O,T>> flatLeft(Function<? super T,? extends Promise<O>> leftFunction)
Transforms the promised value to aPair
, with the value of the result of the given function as theleft
.The function is called with the promised value. The existing promised value will become the
right
.- Type Parameters:
O
- the type of the left value- Parameters:
leftFunction
- a function that produces a promise for the left value from the promised value- Returns:
- a promise
- Since:
- 1.4
-
right
default <O> Promise<Pair<T,O>> right(Promise<O> right)
Transforms the promised value to aPair
, with the value of the given promise as theright
.The existing promised value will become the
left
.- Type Parameters:
O
- the type of the right value- Parameters:
right
- a promise for the right value of the result pair- Returns:
- a promise
-
right
default <O> Promise<Pair<T,O>> right(Function<? super T,? extends O> rightFunction)
Transforms the promised value to aPair
, with the result of the given function as theright
.The function is called with the promised value. The existing promised value will become the
left
.- Type Parameters:
O
- the type of the left value- Parameters:
rightFunction
- a function that produces the right value from the promised value- Returns:
- a promise
- Since:
- 1.4
-
flatRight
default <O> Promise<Pair<T,O>> flatRight(Function<? super T,? extends Promise<O>> rightFunction)
Transforms the promised value to aPair
, with the value of the result of the given function as theright
.The function is called with the promised value. The existing promised value will become the
left
.- Type Parameters:
O
- the type of the left value- Parameters:
rightFunction
- a function that produces a promise for the right value from the promised value- Returns:
- a promise
- Since:
- 1.4
-
operation
default Operation operation()
Converts this promise to an operation, by effectively discarding the result.- Returns:
- an operation
-
operation
default Operation operation(@NonBlocking Action<? super T> action)
Converts this promise to an operation which is effectivelyaction
.- Parameters:
action
- an operation on the promised value- Returns:
- an operation representing
action
-
flatOp
default Operation flatOp(Function<? super T,? extends Operation> function)
Converts this promise to an operation, which is the return offunction
.- Parameters:
function
- a function that returns an operation for the promised value- Returns:
- effectively the return of
function
- Since:
- 1.6
-
mapError
default Promise<T> mapError(Function<? super Throwable,? extends T> transformer)
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.
import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { ExecResult<String> result = ExecHarness.yieldSingle(c -> Promise.<String>error(new Exception("!")) .mapError(e -> "value") ); assertEquals("value", result.getValue()); } }
If the function throws an exception, that exception will now represent the promise failure.
import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { ExecResult<String> result = ExecHarness.yieldSingle(c -> Promise.<String>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.
- Parameters:
transformer
- the transformation to apply to the promise failure- Returns:
- a promise
-
mapError
default <E extends Throwable> Promise<T> mapError(Class<E> type, Function<? super E,? extends T> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.This method is similar to
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.- Parameters:
function
- the transformation to apply to the promise failure- Returns:
- a promise
- Since:
- 1.3
-
mapError
default Promise<T> mapError(Predicate<? super Throwable> predicate, Function<? super Throwable,? extends T> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.This method is similar to
mapError(Function)
, except that it will only apply depending if it satisfies the predicate. If the error is not of the given type, it will not be transformed and will propagate as normal.- Parameters:
predicate
- the predicate to test against the errorfunction
- the transformation to apply to the promise failure- Returns:
- a promise
- Since:
- 1.6.0
-
flatMapError
default Promise<T> flatMapError(Function<? super Throwable,? extends Promise<T>> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.This method is similar to
mapError(Function)
, except that it allows async transformation.- Parameters:
function
- the transformation to apply to the promise failure- Returns:
- a promise
- Since:
- 1.3
-
flatMapError
default <E extends Throwable> Promise<T> flatMapError(Class<E> type, Function<? super E,? extends Promise<T>> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.This method is similar to
mapError(Class, Function)
, except that it allows async transformation.- Parameters:
function
- the transformation to apply to the promise failure- Returns:
- a promise
- Since:
- 1.3
-
flatMapError
default Promise<T> flatMapError(Predicate<? super Throwable> predicate, Function<? super Throwable,? extends Promise<T>> function)
Transforms a failure of the given type (potentially into a value) by applying the given function to it.This method is similar to
mapError(Predicate, Function)
, except that it allows async transformation.- Parameters:
predicate
- the predicate to test against the errorfunction
- the transformation to apply to the promise failure- Returns:
- a promise
- Since:
- 1.6.0
-
apply
default <O> Promise<O> apply(Function<? super Promise<T>,? extends Promise<O>> function)
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.
import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import static org.junit.jupiter.api.Assertions.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<Integer> dubble(Promise<Integer> input) { return input.map(i -> i * 2); } public static Promise<Integer> triple(Promise<Integer> input) { return input.map(i -> i * 3); } }
If the apply function throws an exception, the returned promise will fail.
import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import static org.junit.jupiter.api.Assertions.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<Integer> explode(Promise<Integer> input) throws Exception { throw new Exception("bang!"); } }
If the promise having the operation applied to fails, the operation will not be applied.
import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { Throwable error = ExecHarness.yieldSingle(e -> Promise.<Integer>error(new Exception("bang!")) .apply(Example::dubble) ).getThrowable(); assertEquals("bang!", error.getMessage()); } public static Promise<Integer> dubble(Promise<Integer> input) { return input.map(i -> i * 2); } }
- Type Parameters:
O
- the type of promised object after the operation- Parameters:
function
- the operation implementation- Returns:
- the transformed promise
-
to
default <O> O to(Function<? super Promise<T>,? extends O> function) throws Exception
Applies the given function tothis
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.
import ratpack.rx2.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.jupiter.api.Assertions.assertEquals; public class Example { private static final List<String> LOG = new LinkedList<>(); public static void main(String... args) throws Exception { ExecHarness.runSingle(e -> Promise.value("foo") .to(RxRatpack::single) .doOnSuccess(i -> LOG.add("doOnNext")) .subscribe(i -> LOG.add(i)) ); 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
apply(Function)
for applying custom promise operators.- Type Parameters:
O
- the type the promise will be converted to- Parameters:
function
- the promise conversion function- Returns:
- the output of the given function
- Throws:
Exception
- any thrown by the given function
-
flatMap
default <O> Promise<O> flatMap(Function<? super T,? extends Promise<O>> transformer)
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.
import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import ratpack.exec.Blocking; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String[] args) throws Exception { ExecResult<String> result = ExecHarness.yieldSingle(c -> Promise.value("foo") .flatMap(s -> Blocking.get(s::toUpperCase)) .map(s -> s + "-BAR") ); assertEquals("FOO-BAR", result.getValue()); } }
- Type Parameters:
O
- the type of the transformed object- Parameters:
transformer
- the transformation to apply to the promised value- Returns:
- a promise for the transformed value
-
flatMapIf
default Promise<T> flatMapIf(Predicate<? super T> predicate, Function<? super T,? extends Promise<T>> transformer)
Transforms the promised value by applying the given function to it that returns a promise for the transformed value, if it satisfies the predicate.import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { ExecResult<String> 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()); } }
- Parameters:
predicate
- the condition to satisfy in order to be transformedtransformer
- the transformation to apply to the promised value- Returns:
- a promise
- Since:
- 1.4
-
flatMapIf
default <O> Promise<O> flatMapIf(Predicate<? super T> predicate, Function<? super T,? extends Promise<O>> onTrue, Function<? super T,? extends Promise<O>> onFalse)
Transforms the promised value by applying one of the given functions to it that returns a promise for the transformed value, depending if it satisfies the predicate.import ratpack.test.exec.ExecHarness; import ratpack.exec.ExecResult; import ratpack.exec.Promise; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { ExecResult<String> result = ExecHarness.yieldSingle(c -> Promise.value("foo") .flatMapIf(s -> s.contains("f"), s -> Promise.value(s.toUpperCase()), s -> Promise.value(s)) .flatMapIf(s -> s.contains("f"), s -> Promise.value(s), s -> Promise.value(s + "-BAR")) ); assertEquals("FOO-BAR", result.getValue()); } }
- Parameters:
predicate
- the condition to decide which transformation to applyonTrue
- the transformation to apply to the promised value when the predicate is trueonFalse
- the transformation to apply to the promised value when the predicate is false- Returns:
- a promise
- Since:
- 1.5
-
route
default Promise<T> route(Predicate<? super T> predicate, Action<? super T> action)
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.
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.jupiter.api.Assertions.*; public class Example { public static ExecResult<Integer> yield(int i, List<Integer> 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<Integer> routed = Lists.newLinkedList(); ExecResult<Integer> result1 = yield(1, routed); assertEquals(Integer.valueOf(1), result1.getValue()); assertFalse(result1.isComplete()); // false because promise returned a value before the execution completed assertTrue(routed.isEmpty()); ExecResult<Integer> 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.
import ratpack.exec.Promise; import ratpack.core.handling.Context; import ratpack.test.embed.EmbeddedApp; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static Promise<Integer> 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.
- Parameters:
predicate
- the condition under which the value should be routedaction
- the terminal action for the value- Returns:
- a routed promise
-
onNull
default Promise<T> onNull(Block action)
A convenience shorthand forrouting
null
values.If the promised value is
null
, the given action will be called.- Parameters:
action
- the action to route to if the promised value is null- Returns:
- a routed promise
-
cache
default Promise<T> cache()
Caches the promised value (or error) and returns it to all subscribers.This method is equivalent to using
cacheResultIf(Predicate)
with a predicate that always returnstrue
.import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import java.util.concurrent.atomic.AtomicLong; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { ExecHarness.runSingle(c -> { AtomicLong counter = new AtomicLong(); Promise<Long> 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<Long> 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.
import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import static org.junit.jupiter.api.Assertions.assertTrue; public class Example { public static void main(String... args) throws Exception { ExecHarness.runSingle(c -> { Throwable error = new Exception("bang!"); Promise<Object> cached = Promise.error(error).cache(); cached.onError(t -> assertTrue(t == error)).then(i -> assertTrue(false, "not called")); cached.onError(t -> assertTrue(t == error)).then(i -> assertTrue(false, "not called")); cached.onError(t -> assertTrue(t == error)).then(i -> assertTrue(false, "not called")); }); } }
- Returns:
- a caching promise
- See Also:
cacheIf(Predicate)
,cacheResultIf(Predicate)
,cacheResultFor(Function)
-
cacheIf
default Promise<T> cacheIf(Predicate<? super T> shouldCache)
Caches the promise value and provides it to all future subscribers, if it satisfies the predicate.This method is equivalent to using
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.
- Parameters:
shouldCache
- the test for whether a successful result is cacheable- Returns:
- a caching promise
- Since:
- 1.4
- See Also:
cacheResultIf(Predicate)
,cacheResultFor(Function)
-
cacheResultIf
default Promise<T> cacheResultIf(Predicate<? super ExecResult<T>> shouldCache)
Caches the promise result eternally and provide it to all future subscribers, if it satisfies the predicate.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.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { List<ExecResult<Integer>> results = new ArrayList<>(); AtomicInteger counter = new AtomicInteger(); Promise<Integer> 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<Integer> 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 for a certain amount of time, use
cacheResultFor(Function)
.- Parameters:
shouldCache
- the test for whether a result is cacheable- Returns:
- a caching promise
- Since:
- 1.4
- See Also:
cache()
,cacheIf(Predicate)
,cacheResultFor(Function)
-
cacheResultFor
default Promise<T> cacheResultFor(Function<? super ExecResult<T>,Duration> cacheFor)
Caches the promise result for a calculated amount of time.A cached promise is fully threadsafe and and can be subscribed to concurrently. While there is no valid cached value, yielding the upstream value is serialised. That is, one value is requested at a time regardless of concurrent subscription.
As the result is received, it is given to the
ttlFunc
which determines how long to cache it for. ADuration.ZERO
duration indicates that the value should not be cached. AnyDuration.isNegative()
duration indicates that the value should be cached eternally. Any other duration indicates how long to cache the result for.If the promise is subscribed to again after the cached value has expired, the process repeats.
As such promises tend to be held and reused, it is sometimes necessary to consider garbage collection implications. A caching promise (like all multi-use promises) must retain all of its upstream functions/objects. Care should be taken to ensure that this does not cause long lived references to objects that should be collected.
It is common to use cached promises in conjunction with a cache implementation such as Google Guava or Caffeine.
import ratpack.exec.ExecResult; import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import java.util.ArrayList; import java.util.List; import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { List<Integer> results = new ArrayList<>(); AtomicInteger counter = new AtomicInteger(); Promise<Integer> promise = Promise.sync(counter::getAndIncrement) .cacheResultFor( i -> i.isSuccess() && i.getValue() > 1 ? Duration.ofSeconds(1) : Duration.ZERO ); for (int i = 0; i < 4; ++i) { ExecHarness.runSingle(e -> promise.then(results::add)); } // let the cache entry expire Thread.sleep(1500); for (int i = 0; i < 2; ++i) { ExecHarness.runSingle(e -> promise.then(results::add)); } assertEquals(results.get(0), Integer.valueOf(0)); assertEquals(results.get(1), Integer.valueOf(1)); assertEquals(results.get(2), Integer.valueOf(2)); assertEquals(results.get(3), Integer.valueOf(2)); // cache entry has expired assertEquals(results.get(4), Integer.valueOf(3)); assertEquals(results.get(5), Integer.valueOf(3)); } }
- Parameters:
cacheFor
- a function that determines how long to cache the given result for- Returns:
- a caching promise
- Since:
- 1.5
- See Also:
cache()
,cacheIf(Predicate)
,cacheResultIf(Predicate)
-
defer
default Promise<T> defer(Action<? super Runnable> releaser)
Defers the subscription ofthis
promise until later.When the returned promise is subscribed to, the given
releaser
action will be invoked. The execution ofthis
promise is deferred until the runnable given to thereleaser
is run.It is important to note that this defers the subscription of the promise, not the delivery of the value.
It is generally more convenient to use
throttled(Throttle)
oronYield(Runnable)
than this operation.- Parameters:
releaser
- the action that will initiate the execution some time later- Returns:
- a deferred promise
-
defer
default Promise<T> defer(Duration duration)
Defers the subscription ofthis
promise for the given duration.This operation is roughly the promise based analog of
Execution.sleep(Duration, Block)
.The given duration must be non-negative.
- Parameters:
duration
- the amount of time to defer for- Returns:
- a deferred promise
- Since:
- 1.5
- See Also:
defer(Action)
-
onYield
default Promise<T> onYield(Runnable onYield)
Registers a listener that is invoked whenthis
promise is initiated.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.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { List<String> events = Lists.newLinkedList(); ExecHarness.runSingle(c -> Promise.<String>sync(() -> { events.add("promise"); return "foo"; }) .onYield(() -> events.add("onYield")) .then(v -> events.add("then")) ); assertEquals(Arrays.asList("onYield", "promise", "then"), events); } }
- Parameters:
onYield
- the action to take when the promise is initiated- Returns:
- effectively,
this
promise
-
wiretap
default Promise<T> wiretap(Action<? super Result<T>> listener)
Registers a listener for the promise outcome.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.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { List<String> events = Lists.newLinkedList(); ExecHarness.runSingle(c -> Promise.<String>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); } }
- Parameters:
listener
- the result listener- Returns:
- effectively,
this
promise
-
throttled
default Promise<T> throttled(Throttle throttle)
Throttlesthis
promise, using the giventhrottle
.Throttling can be used to limit concurrency. Typically to limit concurrent use of an external resource, such as a HTTP API.
Note that the
Throttle
instance given defines the actual throttling semantics.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.jupiter.api.Assertions.assertTrue; public class Example { public static void main(String... args) throws Exception { int numJobs = 1000; int maxAtOnce = 10; ExecResult<Integer> 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); } }
- Parameters:
throttle
- the particular throttle to use to throttle the operation- Returns:
- the throttled promise
-
close
default Promise<T> close(AutoCloseable closeable)
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.
import ratpack.exec.Promise; import ratpack.test.exec.ExecHarness; import static org.junit.jupiter.api.Assertions.assertTrue; public class Example { static class MyResource implements AutoCloseable { final boolean inError; boolean closed; public MyResource(boolean inError) { this.inError = inError; } @Override public void close() { closed = true; } } static Promise<String> 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 -> assertTrue(myResource.closed)); }); ExecHarness.runSingle(e -> { MyResource myResource = new MyResource(true); resourceUsingMethod(myResource) .close(myResource) .onError(error -> 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.
- Parameters:
closeable
- the closeable to close- Returns:
- a promise
- Since:
- 1.3
- See Also:
close(Operation)
-
close
default Promise<T> close(Operation closer)
Likeclose(AutoCloseable)
, but allows async close operations.- Parameters:
closer
- the close operation.- Returns:
- a promise
- Since:
- 1.5
-
time
default Promise<T> time(Action<? super Duration> action)
Emits the time taken from when the promise is subscribed to to when the result is available.The given
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.
- Parameters:
action
- a callback for the time- Returns:
- effectively
this
- Since:
- 1.3
- See Also:
timeResult(BiAction)
-
timeResult
default Promise<T> timeResult(BiAction<? super ExecResult<T>,? super Duration> action)
Emits the time taken from when the promise is subscribed to to when the result is available.The given
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.
- Parameters:
action
- a callback for the time- Returns:
- effectively
this
- Since:
- 1.5
- See Also:
time(Action)
-
timeResult
default Promise<Pair<ExecResult<T>,Duration>> timeResult()
-
around
default <B,A> Promise<A> around(Factory<? extends B> before, BiFunction<? super B,? super ExecResult<T>,? extends ExecResult<A>> after)
Facilitates capturing a value before the the promise is subscribed and using it to later augment the result.The
before
factory is invoked as the promise is subscribed. As the promise result becomes available, it and the result are given to theafter
function. The return value of theafter
function forms the basis of the promise returned from this method.- Type Parameters:
B
- the before value typeA
- the after value type- Parameters:
before
- the before value supplierafter
- the after function- Returns:
- a promise
- Since:
- 1.5
-
fork
default Promise<T> fork(Action<? super ExecSpec> execSpec) throws Exception
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
left(Promise)
orright(Promise)
.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.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { CyclicBarrier barrier = new CyclicBarrier(2); Pair<Integer, String> result = ExecHarness.yieldSingle(r -> { Promise<Integer> p1 = Blocking.get(() -> { barrier.await(); return 1; }); Promise<String> p2 = Blocking.get(() -> { barrier.await(); return "2"; }); return p1.right(p2.fork()); }).getValueOrThrow(); assertEquals(result, Pair.of(1, "2")); } }
Warning: be mindful of error handling for forked promises. If the forked promise is never subscribed to, its failure may go unnoticed. In scenarios, where it cannot be guaranteed that the forked promise will be subscribed to or that subscribers would satisfactorily deal with error conditions, consider listening for errors by using
wiretap(Action)
beforefork()
and logging the error or similar.- Parameters:
execSpec
- configuration for the forked execution- Returns:
- a promise
- Throws:
Exception
- any thrown byexecSpec
- Since:
- 1.4
-
fork
default Promise<T> fork()
Forks a new execution and subscribes to this promise, returning a promise for its value.This method delegates to
fork(Action)
withAction.noop()
.- Returns:
- a promise
- Since:
- 1.4
- See Also:
fork(Action)
-
retry
default Promise<T> retry(RetryPolicy retryPolicy, BiAction<? super Integer,? super Throwable> onError)
Causesthis
yielding the promised value to be retried on error, under the rules of providedretryPolicy
.The given function is invoked for each failure, with the sequence number of the failure as the first argument and the failure exception as the second. This may be used to log or collect exceptions. If all errors are to be ignored, use
BiAction.noop()
.Any exception thrown by the function – possibly the exception it receives as an argument – will be propagated to the subscriber, yielding a failure. This can be used to selectively retry on certain failures, but immediately fail on others.
If the promise exhausts the
retryPolicy
, the given function will not be invoked and the most recent exception will propagate.import ratpack.exec.ExecResult; import ratpack.exec.Promise; import ratpack.exec.util.retry.AttemptRetryPolicy; import ratpack.exec.util.retry.RetryPolicy; import ratpack.exec.util.retry.FixedDelay; import ratpack.test.exec.ExecHarness; import java.time.Duration; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { private static final List<String> LOG = new LinkedList<>(); public static void main(String... args) throws Exception { AtomicInteger source = new AtomicInteger(); RetryPolicy retryPolicy = AttemptRetryPolicy.of(b -> b .delay(FixedDelay.of(Duration.ofMillis(500))) .maxAttempts(3)); ExecResult<Integer> result = ExecHarness.yieldSingle(exec -> Promise.sync(source::incrementAndGet) .mapIf(i -> i < 3, i -> { throw new IllegalStateException(); }) .retry(retryPolicy, (i, t) -> LOG.add("retry attempt: " + i)) ); assertEquals(Integer.valueOf(3), result.getValue()); assertEquals(Arrays.asList("retry attempt: 1", "retry attempt: 2"), LOG); } }
- Parameters:
retryPolicy
- policy to govern this retry behaviouronError
- the error handler- Returns:
- a promise with a retry error handler
- Since:
- 1.7
-
retryIf
default Promise<T> retryIf(Predicate<? super Throwable> predicate, RetryPolicy retryPolicy, BiAction<? super Integer,? super Throwable> onError)
Causesthis
yielding the promised value to be retried on error, under the rules of providedretryPolicy
, and if the givenPredicate
matches the error thrown.The given function is invoked for each failure, with the sequence number of the failure as the first argument and the failure exception as the second. This may be used to log or collect exceptions. If all errors are to be ignored, use
BiAction.noop()
.Any exception thrown by the function – possibly the exception it receives as an argument – will be propagated to the subscriber, yielding a failure. This can be used to selectively retry on certain failures, but immediately fail on others.
If the promise exhausts the
retryPolicy
, the given function will not be invoked and the most recent exception will propagate.import ratpack.exec.ExecResult; import ratpack.exec.Promise; import ratpack.exec.util.retry.AttemptRetryPolicy; import ratpack.exec.util.retry.RetryPolicy; import ratpack.exec.util.retry.FixedDelay; import ratpack.test.exec.ExecHarness; import java.time.Duration; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { private static final List<String> LOG = new LinkedList<>(); public static void main(String... args) throws Exception { AtomicInteger source = new AtomicInteger(); RetryPolicy retryPolicy = AttemptRetryPolicy.of(b -> b .delay(FixedDelay.of(Duration.ofMillis(500))) .maxAttempts(3)); ExecResult<Integer> result = ExecHarness.yieldSingle(exec -> Promise.sync(source::incrementAndGet) .mapIf(i -> i < 3, i -> { throw new IllegalStateException(); }) .retryIf(t -> t instanceof IllegalStateException, retryPolicy, (i, t) -> LOG.add("retry attempt: " + i)) ); assertEquals(Integer.valueOf(3), result.getValue()); assertEquals(Arrays.asList("retry attempt: 1", "retry attempt: 2"), LOG); } }
- Parameters:
predicate
- the predicate against which thrown errors are matched. If the predicate succeeds, the retry is allowed to executeretryPolicy
- policy to govern this retry behaviouronError
- the error handler- Returns:
- a promise with a retry error handler
- Since:
- 1.8
-
toCompletableFuture
default CompletableFuture<T> toCompletableFuture()
Convert this promise into aCompletableFuture
.- Returns:
- a
CompletableFuture
that will complete successfully or exceptionally on the current execution thread. - Since:
- 1.6
-
onComplete
default Promise<T> onComplete(Block block)
Specifies the action to take if theUpstream
signals complete without emitting a value or an error.If the given action throws an exception, the exception will be propagated to the Promise's
onError(ratpack.func.Predicate<? super java.lang.Throwable>, ratpack.func.Action<? super java.lang.Throwable>)
method.- Parameters:
block
- the action to take ifUpstream
signalscomplete
- Returns:
- a promise with an action to execute on complete
- Since:
- 1.8
-
toPromise
static <T> Promise<T> toPromise(CompletableFuture<T> future)
Convert aCompletableFuture
into a promise.- Type Parameters:
T
- The type of the promised value- Parameters:
future
- theCompletableFuture
to convert into aPromise
- Returns:
- a
Promise
that will be consumed on the current execution thread. - Since:
- 1.6
-
-