code: 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;
      }
docs: 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.