public static <T> CompletableFuture<Void> loop(Supplier<Boolean> condition, Supplier<CompletableFuture<T>> loopBody, Consumer<T> resultConsumer, Executor executor) {
        CompletableFuture<Void> result = new CompletableFuture<>();
        Loop<T> loop = new Loop<>(condition, loopBody, resultConsumer, result, executor);
        executor.execute(loop);
        return result;
    }
public static < T > CompletableFuture < Void > loop ( Supplier < Boolean > condition , Supplier < CompletableFuture < T > > loopBody , Consumer < T > resultConsumer , Executor executor ) { CompletableFuture < Void > result = new CompletableFuture < > ( ) ; Loop < T > loop = new Loop < > ( condition , loopBody , resultConsumer , result , executor ) ; executor . execute ( loop ) ; return result ; }
Executes a loop using CompletableFutures, without invoking join()/get() on any of them or exclusively hogging a thread.

@param condition      A Supplier 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 resultConsumer A Consumer that will be invoked with the result of every call to loopBody.
@param executor       An Executor that is used to execute the condition and the loop support code.
@param <T>            The Type of the future's result.
@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.