public final class AsyncTrampoline extends Object
When working with CompletionStage, it's often desirable to have a loop like construct
which keeps producing stages until some condition is met. Because continuations are asynchronous,
it's usually easiest to do this with a recursive approach:
CompletionStage<Integer> getNextNumber();
CompletionStage<Integer> getFirstOddNumber(int current) {
if (current % 2 != 0)
// found odd number
return CompletableFuture.completedFuture(current);
else
// get the next number and recurse
return getNextNumber().thenCompose(next -> getFirstOddNumber(next));
}
The problem with this is that if the implementation of getNextNumber happens to be synchronous
CompletionStage<Integer> getNextNumber() {
return CompletableFuture.completedFuture(random.nextInt());
}
then getFirstOddNumber can easily cause a stack overflow. This situation often happens when a
cache is put under an async API, and all the values are cached and returned immediately. This
could be avoided by scheduling the recursive calls back to a thread pool using
CompletionStage.thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>), however the overhead of the thread pool submissions may
be high and may cause unnecessary context switching.
The methods on this class ensure that the stack doesn't blow up - if multiple calls happen on the same thread they are queued and run in a loop. You could write the previous example like this:
CompletionStage<Integer> getFirstOddNumber(int initial) {
return AsyncTrampoline.asyncWhile(
i -> i % 2 == 0,
i -> getNextNumber(),
initial);
}
Though this class provides efficient methods for a few loop patterns, many are better represented
by the more expressive API available on AsyncIterator, which is also stack safe. For
example, the preceding snippet can be expressed as
AsyncIterator.generate(this::getNextNumber).find(i -> i % 2 != 0)AsyncIterator| Modifier and Type | Method | Description |
|---|---|---|
static <T> CompletionStage<T> |
asyncDoWhile(Function<? super T,? extends CompletionStage<T>> fn,
T initialValue,
Predicate<? super T> shouldContinue) |
Repeatedly applies an asynchronous function
fn to a value until shouldContinue
returns false, unconditionally applying fn to initialValue on the first
iteration. |
static <T> CompletionStage<T> |
asyncWhile(Predicate<? super T> shouldContinue,
Function<? super T,? extends CompletionStage<T>> fn,
T initialValue) |
Repeatedly applies an asynchronous function
fn to a value until shouldContinue
returns false. |
static CompletionStage<Void> |
asyncWhile(Supplier<? extends CompletionStage<Boolean>> fn) |
Repeatedly uses the function
fn to produce a CompletionStage of a boolean,
stopping when then boolean is false. |
public static <T> CompletionStage<T> asyncWhile(Predicate<? super T> shouldContinue, Function<? super T,? extends CompletionStage<T>> fn, T initialValue)
fn to a value until shouldContinue
returns false. The asynchronous equivalent of
T loop(Predicate shouldContinue, Function fn, T initialValue) {
T t = initialValue;
while (shouldContinue.test(t)) {
t = fn.apply(t);
}
return t;
}
Effectively produces fn(seed).thenCompose(fn).thenCompose(fn)... .thenCompose(fn) until
an value fails the predicate. Note that predicate will be applied on seed (like a while loop,
the initial value is tested). For a do/while style see
asyncDoWhile(Function, Object, Predicate). If the predicate or fn throw an exception,
or the CompletionStage returned by fn completes exceptionally, iteration will stop and
an exceptional stage will be returned.
T - the type of elements produced by the loopshouldContinue - a predicate which will be applied to every intermediate T value
(including the initialValue) until it fails and looping stops.fn - the function for the loop body which produces a new CompletionStage based on
the result of the previous iteration.initialValue - the value that will initially be passed to fn, it will also be
initially tested by shouldContinueCompletionStage that completes with the first value t such that
shouldContinue.test(T) == false, or with an exception if one was thrown.public static CompletionStage<Void> asyncWhile(Supplier<? extends CompletionStage<Boolean>> fn)
fn to produce a CompletionStage of a boolean,
stopping when then boolean is false. The asynchronous equivalent of
while(fn.get());. Generally, the function fn must perform some side effect for this method to
be useful. If the fn throws or produces an exceptional CompletionStage, an
exceptional stage will be returned.fn - a Supplier of a CompletionStage that indicates whether iteration
should continueCompletionStage that is complete when a stage produced by fn has
returned false, or with an exception if one was thrownpublic static <T> CompletionStage<T> asyncDoWhile(Function<? super T,? extends CompletionStage<T>> fn, T initialValue, Predicate<? super T> shouldContinue)
fn to a value until shouldContinue
returns false, unconditionally applying fn to initialValue on the first
iteration. The asynchronous equivalent of
{
T t = initialValue;
do {
t = fn.apply(t);
} while(shouldContinue.test(t));
return t;
}
Effectively produces fn(seed).thenCompose(fn).thenCompose(fn)... .thenCompose(fn) until
an value fails the predicate. Note that predicate will be applied on seed (like a while loop,
Effectively produces fn(seed).then(fn).flatMap(fn)... .flatMap(fn) until an value fails the
predicate. Note that predicate will not be applied to initialValue, for a while
style loop see asyncWhile(Predicate, Function, Object) . If the predicate or fn throw
an exception, or the CompletionStage returned by fn completes exceptionally, iteration
will stop and an exceptional stage will be returned.
T - the type of elements produced by the loopshouldContinue - a predicate which will be applied to intermediate T value other than the
initialValue the in until it fails and looping stops.fn - the function for the loop body which produces a new CompletionStage based on
the result of the previous iteration.initialValue - the value that will initially be passed to fn, it will not be
initially tested by shouldContinueCompletionStage that completes with the first value t such that
shouldContinue.test(T) == false, or with an exception if one was thrown.Copyright © 2018. All rights reserved.