public static <T> CompletableFuture<Void> doWhileLoop(Supplier<CompletableFuture<T>> loopBody, Predicate<T> condition, Executor executor) {
        CompletableFuture<Void> result = new CompletableFuture<>();

        // We implement the do-while loop using a regular loop, but we execute one iteration before we create the actual Loop object.
        // Since this method has slightly different arguments than loop(), we need to make one adjustment:
        // * After each iteration, we get the result and run it through 'condition' and use that to decide whether to continue.
        AtomicBoolean canContinue = new AtomicBoolean();
        Consumer<T> iterationResultHandler = ir -> canContinue.set(condition.test(ir));
        loopBody.get()
                .thenAccept(iterationResultHandler)
                .thenRunAsync(() -> {
                    Loop<T> loop = new Loop<>(canContinue::get, loopBody, iterationResultHandler, result, executor);
                    executor.execute(loop);
                }, executor)
                .exceptionally(ex -> {
                    // Handle exceptions from the first iteration.
                    result.completeExceptionally(ex);
                    return null;
                });
        return result;
    }
public static < T > CompletableFuture < Void > doWhileLoop ( Supplier < CompletableFuture < T > > loopBody , Predicate < T > condition , Executor executor ) { CompletableFuture < Void > result = new CompletableFuture < > ( ) ; AtomicBoolean canContinue = new AtomicBoolean ( ) ; Consumer < T > iterationResultHandler = ir -> canContinue . set ( condition . test ( ir ) ) ; loopBody . get ( ) . thenAccept ( iterationResultHandler ) . thenRunAsync ( ( ) -> { Loop < T > loop = new Loop < > ( canContinue :: get , loopBody , iterationResultHandler , result , executor ) ; executor . execute ( loop ) ; } , executor ) . exceptionally ( ex -> { result . completeExceptionally ( ex ) ; return null ; } ) ; return result ; }
Executes a code fragment returning a CompletableFutures while a condition on the returned value is satisfied.

@param condition Predicate that indicates whether to proceed with the loop or not.
@param loopBody  A Supplier that returns a CompletableFuture which represents the body of the loop. This
supplier is invoked every time the loopBody needs to execute.
@param executor  An Executor that is used to execute the condition and the loop support code.
@param <T>       Return type of the executor.
@return A CompletableFuture that, when completed, indicates the loop terminated without any exception. If
either the loopBody or condition throw/return Exceptions, these will be set as the result of this returned Future.