T
- the type of promised valuepublic interface Promise<T>
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 the transform(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 as sync(Factory)
.
To test code that uses promises, use the ExecHarness
.
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.
The cache()
operation can be used to change this behaviour.
Modifier and Type | Method and 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)
Like
map(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>,java.time.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(java.lang.AutoCloseable closeable)
Closes the given closeable when the value or error propagates to this point.
|
default Promise<T> |
close(Operation closer)
Like
close(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(Action<? super java.lang.Runnable> releaser)
Defers the subscription of
this promise until later. |
default Promise<T> |
defer(java.time.Duration duration)
Defers the subscription of
this promise for the given duration. |
static <T> Promise<T> |
error(java.lang.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 a
Pair , with the value of the result of the given function as the left . |
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 java.lang.Throwable> |
flatMapError(java.lang.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 java.lang.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 <O> Promise<Pair<T,O>> |
flatRight(Function<? super T,? extends Promise<O>> rightFunction)
Transforms the promised value to a
Pair , with the value of the result of the given function as the right . |
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(Function<? super T,? extends O> leftFunction)
Transforms the promised value to a
Pair , with the result of the given function as the left . |
default <O> Promise<Pair<O,T>> |
left(Promise<O> left)
Transforms the promised value to a
Pair , with the value of the given promise as the left . |
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 java.lang.Throwable> |
mapError(java.lang.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 java.lang.Throwable,? extends T> transformer)
Transforms the promise failure (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 <O> Promise<O> |
next(Promise<O> next)
Deprecated.
replaced by
replace(Promise) as of 1.1.0 |
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> |
of(Upstream<T> upstream)
Deprecated.
replaced by
async(Upstream) |
static <T> Promise<T> |
ofLazy(Factory<T> factory)
Deprecated.
replaced by
sync(Factory) } |
static <T> Promise<T> |
ofNull()
A promise for
null . |
default Promise<T> |
onError(Action<? super java.lang.Throwable> errorHandler)
Specifies the action to take if the an error occurs trying to produce the promised value.
|
default <E extends java.lang.Throwable> |
onError(java.lang.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(Predicate<? super java.lang.Throwable> predicate,
Action<? super java.lang.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 for
routing null values. |
default Promise<T> |
onYield(java.lang.Runnable onYield)
Registers a listener that is invoked when
this promise is initiated. |
default Operation |
operation()
Converts this promise to an operation, by effectively discarding the result.
|
default Operation |
operation(Action<? super T> action) |
default <O> Promise<O> |
replace(Promise<O> next)
Replaces
this promise with the provided promise for downstream subscribers. |
default void |
result(Action<? super ExecResult<T>> resultHandler)
Consume the promised value as a
Result . |
default Promise<T> |
retry(int maxAttempts,
BiFunction<? super java.lang.Integer,? super java.lang.Throwable,Promise<java.time.Duration>> onError)
Causes
this yielding the promised value to be retried on error, after a calculated delay. |
default Promise<T> |
retry(int maxAttempts,
java.time.Duration delay,
BiAction<? super java.lang.Integer,? super java.lang.Throwable> onError)
Causes
this yielding the promised value to be retried on error, after a fixed delay. |
default <O> Promise<Pair<T,O>> |
right(Function<? super T,? extends O> rightFunction)
Transforms the promised value to a
Pair , with the result of the given function as the right . |
default <O> Promise<Pair<T,O>> |
right(Promise<O> right)
Transforms the promised value to a
Pair , with the value of the given promise as the right . |
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)
Throttles
this promise, using the given throttle . |
default Promise<T> |
time(Action<? super java.time.Duration> action)
Emits the time taken from when the promise is subscribed to to when the result is available.
|
default Promise<Pair<ExecResult<T>,java.time.Duration>> |
timeResult() |
default Promise<T> |
timeResult(BiAction<? super ExecResult<T>,? super java.time.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 to
this and returns the result. |
<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.
|
static <T> Promise<T> |
wrap(Factory<? extends Promise<T>> factory)
Deprecated.
since 1.5, replaced by
flatten(Factory) . |
static <T> Promise<T> async(Upstream<T> upstream)
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.Assert.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");
}
}
T
- the type of promised valueupstream
- the producer of the valueUpstream
,
sync(Factory)
,
value(Object)
,
error(Throwable)
static <T> Promise<T> sync(Factory<T> 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.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.
T
- the type of promised valuefactory
- the producer of the valueasync(Upstream)
,
value(Object)
,
error(Throwable)
static <T> Promise<T> flatten(Factory<? extends Promise<T>> 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.
T
- the type of promised valuefactory
- the producer of the promise to return@Deprecated static <T> Promise<T> wrap(Factory<? extends Promise<T>> factory)
flatten(Factory)
.flatten(Factory)
.T
- the type of promised valuefactory
- deprecated.static <T> Promise<T> value(T t)
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.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");
}
}
T
- the type of promised valuet
- the promised valueasync(Upstream)
,
sync(Factory)
,
error(Throwable)
static <T> Promise<T> ofNull()
null
.T
- the type of promised valuenull
.static <T> Promise<T> error(java.lang.Throwable t)
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.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);
}
}
T
- the type of promised valuet
- the errorasync(Upstream)
,
sync(Factory)
,
value(Object)
@Deprecated static <T> Promise<T> of(Upstream<T> upstream)
async(Upstream)
T
- the type of promised valueupstream
- the producer of the value@Deprecated static <T> Promise<T> ofLazy(Factory<T> factory)
sync(Factory)
}T
- the type of promised valuefactory
- the producer of the valuevoid then(Action<? super T> then)
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
.
then
- the receiver of the promised valueExecutionException
- if not called on a Ratpack managed compute threadvoid connect(Downstream<? super T> downstream)
It is generally preferable to use then(Action)
over this method.
downstream
- the downstream consumer<O> Promise<O> transform(Function<? super Upstream<? extends T>,? extends Upstream<O>> upstreamTransformer)
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.Assert.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.
O
- the type of item emitted by the transformed upstreamupstreamTransformer
- a function that returns a new upstream, typically wrapping the given upstream argumentdefault Promise<T> onError(Predicate<? super java.lang.Throwable> predicate, Action<? super java.lang.Throwable> errorHandler)
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.
predicate
- the predicate to test against the errorerrorHandler
- the action to take if an error occursdefault <E extends java.lang.Throwable> Promise<T> onError(java.lang.Class<E> errorType, Action<? super E> errorHandler)
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.
E
- the type of exception to handle with the given actionerrorType
- the type of exception to handle with the given actionerrorHandler
- the action to take if an error occursdefault Promise<T> onError(Action<? super java.lang.Throwable> errorHandler)
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.
errorHandler
- the action to take if an error occursdefault void result(Action<? super ExecResult<T>> resultHandler)
Result
.
This method is an alternative to then(Action)
and onError(Action)
.
resultHandler
- the consumer of the resultdefault <O> Promise<O> map(Function<? super T,? extends O> transformer)
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<String> result = ExecHarness.yieldSingle(c ->
Promise.value("foo")
.map(String::toUpperCase)
.map(s -> s + "-BAR")
);
assertEquals("FOO-BAR", result.getValue());
}
}
O
- the type of the transformed objecttransformer
- the transformation to apply to the promised valuedefault Promise<T> mapIf(Predicate<? super T> predicate, Function<? super T,? extends T> transformer)
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<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());
}
}
predicate
- the condition to satisfy in order to be transformedtransformer
- the transformation to apply to the promised valuedefault <O> Promise<O> mapIf(Predicate<? super T> predicate, Function<? super T,? extends O> onTrue, Function<? super T,? extends O> onFalse)
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<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());
}
}
O
- the type of the transformed objectpredicate
- 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 falsedefault <O> Promise<O> blockingMap(Function<? super T,? extends O> transformer)
map(Function)
, but performs the transformation on a blocking thread.
This is simply a more convenient form of using Blocking.get(Factory)
and flatMap(Function)
.
O
- the type of the transformed objecttransformer
- the transformation to apply to the promised value, on a blocking threaddefault Promise<T> blockingOp(Action<? super T> action)
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.
action
- the action to to perform with the value, on a blocking thread@Deprecated default <O> Promise<O> next(Promise<O> next)
replace(Promise)
as of 1.1.0
Use replace(Promise)
.
O
- the type of value provided by the replacement promisenext
- the promise to replace this
withdefault Promise<T> next(@NonBlocking Action<? super T> action)
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.Assert.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);
}
}
action
- the action to execute with the promised valuenextOp(Function)
default Promise<T> nextOp(Function<? super T,? extends Operation> 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.Assert.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);
}
}
function
- a function that returns an operation that acts on the promised valuenext(Action)
default Promise<T> nextOpIf(Predicate<? super T> predicate, Function<? super T,? extends Operation> 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.Assert.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);
}
}
predicate
- the condition to satisfy in order to execute the operation.function
- a function that returns an operation that acts on the promised valuedefault <O> Promise<O> replace(Promise<O> next)
this
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.Assert.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);
}
}
O
- the type of the value of the replacement promisenext
- the promise to replace this
withdefault <O> Promise<Pair<O,T>> left(Promise<O> left)
Pair
, with the value of the given promise as the left
.
The existing promised value will become the right
.
O
- the type of the left valueleft
- a promise for the left value of the result pairdefault <O> Promise<Pair<O,T>> left(Function<? super T,? extends O> leftFunction)
Pair
, with the result of the given function as the left
.
The function is called with the promised value.
The existing promised value will become the right
.
O
- the type of the left valueleftFunction
- a function that produces the left value from the promised valuedefault <O> Promise<Pair<O,T>> flatLeft(Function<? super T,? extends Promise<O>> leftFunction)
Pair
, with the value of the result of the given function as the left
.
The function is called with the promised value.
The existing promised value will become the right
.
O
- the type of the left valueleftFunction
- a function that produces a promise for the left value from the promised valuedefault <O> Promise<Pair<T,O>> right(Promise<O> right)
Pair
, with the value of the given promise as the right
.
The existing promised value will become the left
.
O
- the type of the right valueright
- a promise for the right value of the result pairdefault <O> Promise<Pair<T,O>> right(Function<? super T,? extends O> rightFunction)
Pair
, with the result of the given function as the right
.
The function is called with the promised value.
The existing promised value will become the left
.
O
- the type of the left valuerightFunction
- a function that produces the right value from the promised valuedefault <O> Promise<Pair<T,O>> flatRight(Function<? super T,? extends Promise<O>> rightFunction)
Pair
, with the value of the result of the given function as the right
.
The function is called with the promised value.
The existing promised value will become the left
.
O
- the type of the left valuerightFunction
- a function that produces a promise for the right value from the promised valuedefault Operation operation()
default Operation operation(@NonBlocking Action<? super T> action)
default Promise<T> mapError(Function<? super java.lang.Throwable,? extends T> transformer)
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.Assert.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.Assert.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.
transformer
- the transformation to apply to the promise failuredefault <E extends java.lang.Throwable> Promise<T> mapError(java.lang.Class<E> type, Function<? super E,? extends T> function)
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.
function
- the transformation to apply to the promise failuredefault Promise<T> flatMapError(Function<? super java.lang.Throwable,? extends Promise<T>> function)
This method is similar to mapError(Function)
, except that it allows async transformation.
function
- the transformation to apply to the promise failuredefault <E extends java.lang.Throwable> Promise<T> flatMapError(java.lang.Class<E> type, Function<? super E,? extends Promise<T>> function)
This method is similar to mapError(Class, Function)
, except that it allows async transformation.
function
- the transformation to apply to the promise failuredefault <O> Promise<O> apply(Function<? super Promise<T>,? extends Promise<O>> function)
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.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<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.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<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.Assert.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);
}
}
O
- the type of promised object after the operationfunction
- the operation implementationdefault <O> O to(Function<? super Promise<T>,? extends O> function) throws java.lang.Exception
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.
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<String> 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 apply(Function)
for applying custom promise operators.
O
- the type the promise will be converted tofunction
- the promise conversion functionjava.lang.Exception
- any thrown by the given functiondefault <O> Promise<O> flatMap(Function<? super T,? extends Promise<O>> transformer)
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.Assert.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());
}
}
O
- the type of the transformed objecttransformer
- the transformation to apply to the promised valuedefault Promise<T> flatMapIf(Predicate<? super T> predicate, Function<? super T,? extends Promise<T>> transformer)
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<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());
}
}
predicate
- the condition to satisfy in order to be transformedtransformer
- the transformation to apply to the promised valuedefault <O> Promise<O> flatMapIf(Predicate<? super T> predicate, Function<? super T,? extends Promise<O>> onTrue, Function<? super T,? extends Promise<O>> onFalse)
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<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());
}
}
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 falsedefault Promise<T> route(Predicate<? super T> predicate, Action<? super T> action)
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.Assert.*;
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(new Integer(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.handling.Context;
import ratpack.test.embed.EmbeddedApp;
import static org.junit.Assert.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.
predicate
- the condition under which the value should be routedaction
- the terminal action for the valuedefault Promise<T> onNull(Block action)
routing
null
values.
If the promised value is null
, the given action will be called.
action
- the action to route to if the promised value is nulldefault Promise<T> cache()
This method is equivalent to using cacheResultIf(Predicate)
with a predicate that always returns true
.
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<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.Assert.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("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));
});
}
}
cacheIf(Predicate)
,
cacheResultIf(Predicate)
,
cacheResultFor(Function)
default Promise<T> cacheIf(Predicate<? super T> shouldCache)
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.
shouldCache
- the test for whether a successful result is cacheablecacheResultIf(Predicate)
,
cacheResultFor(Function)
default Promise<T> cacheResultIf(Predicate<? super ExecResult<T>> shouldCache)
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<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)
.
shouldCache
- the test for whether a result is cacheablecache()
,
cacheIf(Predicate)
,
cacheResultFor(Function)
default Promise<T> cacheResultFor(Function<? super ExecResult<T>,java.time.Duration> cacheFor)
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.
A Duration.ZERO
duration indicates that the value should not be cached.
Any Duration.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.Assert.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));
}
}
cacheFor
- a function that determines how long to cache the given result forcache()
,
cacheIf(Predicate)
,
cacheResultIf(Predicate)
default Promise<T> defer(Action<? super java.lang.Runnable> releaser)
this
promise until later.
When the returned promise is subscribed to, the given releaser
action will be invoked.
The execution of this
promise is deferred until the runnable given to the releaser
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)
or onYield(Runnable)
than this operation.
releaser
- the action that will initiate the execution some time laterdefault Promise<T> defer(java.time.Duration duration)
this
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.
duration
- the amount of time to defer fordefer(Action)
default Promise<T> onYield(java.lang.Runnable onYield)
this
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.Assert.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);
}
}
onYield
- the action to take when the promise is initiatedthis
promisedefault Promise<T> wiretap(Action<? super Result<T>> listener)
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<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);
}
}
listener
- the result listenerthis
promisedefault Promise<T> throttled(Throttle throttle)
this
promise, using the given 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 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.Assert.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);
}
}
throttle
- the particular throttle to use to throttle the operationdefault Promise<T> close(java.lang.AutoCloseable closeable)
This can be used to simulate a try/finally synchronous construct. It is typically used to close some resource after an asynchronous operation.
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;
}
@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 -> 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.
closeable
- the closeable to closeclose(Operation)
default Promise<T> close(Operation closer)
close(AutoCloseable)
, but allows async close operations.closer
- the close operation.default Promise<T> time(Action<? super java.time.Duration> action)
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.
action
- a callback for the timethis
timeResult(BiAction)
default Promise<T> timeResult(BiAction<? super ExecResult<T>,? super java.time.Duration> action)
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.
action
- a callback for the timethis
time(Action)
default Promise<Pair<ExecResult<T>,java.time.Duration>> timeResult()
default <B,A> Promise<A> around(Factory<? extends B> before, BiFunction<? super B,? super ExecResult<T>,? extends ExecResult<A>> after)
The before
factory is invoked as the promise is subscribed.
As the promise result becomes available, it and the result are given to the after
function.
The return value of the after
function forms the basis of the promise returned from this method.
B
- the before value typeA
- the after value typebefore
- the before value supplierafter
- the after functiondefault Promise<T> fork(Action<? super ExecSpec> execSpec) throws java.lang.Exception
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)
or right(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.Assert.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)
before fork()
and logging the error or similar.
execSpec
- configuration for the forked executionjava.lang.Exception
- any thrown by execSpec
default Promise<T> fork()
This method delegates to fork(Action)
with Action.noop()
.
fork(Action)
default Promise<T> retry(int maxAttempts, java.time.Duration delay, @NonBlocking BiAction<? super java.lang.Integer,? super java.lang.Throwable> onError)
this
yielding the promised value to be retried on error, after a fixed delay.
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 fails maxAttempts
times,
the given function will not be invoked and the most and the most recent exception will propagate.
To retry immediately, pass a zero duration.
Use retry(int, BiFunction)
if desiring a dynamic (e.g. increasing) delay between attempts.
import ratpack.exec.ExecResult;
import ratpack.exec.Promise;
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.Assert.assertEquals;
public class Example {
private static final List<String> LOG = new LinkedList<>();
public static void main(String... args) throws Exception {
AtomicInteger source = new AtomicInteger();
ExecResult<Integer> result = ExecHarness.yieldSingle(exec ->
Promise.sync(source::incrementAndGet)
.mapIf(i -> i < 3, i -> { throw new IllegalStateException(); })
.retry(3, Duration.ofMillis(500), (i, t) -> LOG.add("retry attempt: " + i))
);
assertEquals(Integer.valueOf(3), result.getValue());
assertEquals(Arrays.asList("retry attempt: 1", "retry attempt: 2"), LOG);
}
}
maxAttempts
- the maximum number of times to retrydelay
- the duration to wait between retry attemptsonError
- the error handlerretry(int, BiFunction)
default Promise<T> retry(int maxAttempts, BiFunction<? super java.lang.Integer,? super java.lang.Throwable,Promise<java.time.Duration>> onError)
this
yielding the promised value to be retried on error, after a calculated delay.
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.
It should return the duration of time to wait before retrying.
To retry immediately, return a zero duration.
Use retry(int, Duration, BiAction)
if desiring a fixed delay between attempts.
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 fails maxAttempts
times,
the given function will not be invoked and the most and the most recent exception will propagate.
import ratpack.exec.ExecResult;
import ratpack.exec.Promise;
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.Assert.assertEquals;
public class Example {
private static final List<String> LOG = new LinkedList<>();
public static void main(String... args) throws Exception {
AtomicInteger source = new AtomicInteger();
ExecResult<Integer> result = ExecHarness.yieldSingle(exec ->
Promise.sync(source::incrementAndGet)
.mapIf(i -> i < 3, i -> { throw new IllegalStateException(); })
.retry(3, (i, t) -> {
LOG.add("retry attempt: " + i);
return Promise.value(Duration.ofMillis(500 * i));
})
);
assertEquals(Integer.valueOf(3), result.getValue());
assertEquals(Arrays.asList("retry attempt: 1", "retry attempt: 2"), LOG);
}
}
maxAttempts
- the maximum number of times to retryonError
- the error handlerretry(int, Duration, BiAction)