Class AsyncTrampoline
- java.lang.Object
-
- org.hibernate.reactive.util.async.impl.AsyncTrampoline
-
public final class AsyncTrampoline extends java.lang.Object
Copy of com.ibm.asyncutil.iteration.AyncTrampoline from com.ibm.async:asyncutil:0.1.0 without all the methods and imports we don't need for Hibernate Reactive.Static methods for asynchronous looping procedures without exhausting the stack.
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 asAsyncIterator.generate(this::getNextNumber).find(i -> i % 2 != 0)
- See Also:
AsyncIterator
-
-
Method Summary
All Methods Static Methods Concrete Methods Modifier and Type Method Description static <T> java.util.concurrent.CompletionStage<T>
asyncWhile(java.util.function.Predicate<? super T> shouldContinue, java.util.function.Function<? super T,? extends java.util.concurrent.CompletionStage<T>> fn, T initialValue)
Repeatedly applies an asynchronous functionfn
to a value untilshouldContinue
returnsfalse
.static java.util.concurrent.CompletionStage<java.lang.Void>
asyncWhile(java.util.function.Supplier<? extends java.util.concurrent.CompletionStage<java.lang.Boolean>> fn)
Repeatedly uses the functionfn
to produce aCompletionStage
of a boolean, stopping when then boolean isfalse
.
-
-
-
Method Detail
-
asyncWhile
public static <T> java.util.concurrent.CompletionStage<T> asyncWhile(java.util.function.Predicate<? super T> shouldContinue, java.util.function.Function<? super T,? extends java.util.concurrent.CompletionStage<T>> fn, T initialValue)
Repeatedly applies an asynchronous functionfn
to a value untilshouldContinue
returnsfalse
. The asynchronous equivalent ofT 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). If the predicate or fn throw an exception, or theCompletionStage
returned by fn completes exceptionally, iteration will stop and an exceptional stage will be returned.- Type Parameters:
T
- the type of elements produced by the loop- Parameters:
shouldContinue
- a predicate which will be applied to every intermediate T value (including theinitialValue
) until it fails and looping stops.fn
- the function for the loop body which produces a newCompletionStage
based on the result of the previous iteration.initialValue
- the value that will initially be passed tofn
, it will also be initially tested byshouldContinue
- Returns:
- a
CompletionStage
that completes with the first value t such thatshouldContinue.test(T) == false
, or with an exception if one was thrown.
-
asyncWhile
public static java.util.concurrent.CompletionStage<java.lang.Void> asyncWhile(java.util.function.Supplier<? extends java.util.concurrent.CompletionStage<java.lang.Boolean>> fn)
Repeatedly uses the functionfn
to produce aCompletionStage
of a boolean, stopping when then boolean isfalse
. The asynchronous equivalent ofwhile(fn.get());
. Generally, the function fn must perform some side effect for this method to be useful. If thefn
throws or produces an exceptionalCompletionStage
, an exceptional stage will be returned.- Parameters:
fn
- aSupplier
of aCompletionStage
that indicates whether iteration should continue- Returns:
- a
CompletionStage
that is complete when a stage produced byfn
has returnedfalse
, or with an exception if one was thrown
-
-