code: public static <T> CompletableFuture<Void> loop(Iterable<T> iterable, Function<T, CompletableFuture<Boolean>> loopBody, Executor executor) {
          Iterator<T> iterator = iterable.iterator();
          AtomicBoolean canContinue = new AtomicBoolean(true);
          return loop(
                  () -> iterator.hasNext() && canContinue.get(),
                  () -> loopBody.apply(iterator.next()),
                  canContinue::set, executor);
      }
docs: Executes a loop using CompletableFutures over the given Iterable, processing each item in order, without overlap,
  using the given Executor for task execution.
  
  @param iterable An Iterable instance to loop over.
  @param loopBody A Function that, when applied to an element in the given iterable, returns a CompletableFuture which
  will complete when the given element has been processed. This function 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>      Type of items in the given iterable.
  @return A CompletableFuture that, when completed, indicates the loop terminated without any exception. If
  the loopBody throws/returns Exceptions, these will be set as the result of this returned Future.