All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.pincette.rs.AsyncDepend Maven / Gradle / Ivy

package net.pincette.rs;

import static net.pincette.rs.Serializer.dispatch;
import static net.pincette.rs.Util.initialStageDeque;

import java.util.Deque;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow.Processor;
import java.util.function.BiFunction;
import net.pincette.function.SideEffect;

/**
 * Emits the values produced by functions in the order the values arrive. The functions also receive
 * the result of the previous call, or null if it is the first call. The stream
 * completes only after the last stage has completed.
 *
 * @param  the incoming value type.
 * @param  the outgoing value type.
 * @author Werner Donn\u00e9
 * @since 3.0
 */
public class AsyncDepend extends ProcessorBase {
  private final BiFunction> function;
  private final Deque> stages = initialStageDeque();

  public AsyncDepend(final BiFunction> function) {
    this.function = function;
  }

  /**
   * Returns a processor with the mapping function, which transforms the objects. The functions
   * stages are executed in the order of the stream, which completes only after the last stage is
   * completed. A function call will also receive the result of the previous call, which is 
   * null for the first call.
   *
   * @param function the mapping function.
   * @param  the incoming value type.
   * @param  the outgoing value type.
   * @return The processor.
   */
  public static  Processor mapAsync(
      final BiFunction> function) {
    return new AsyncDepend<>(function);
  }

  @Override
  protected void emit(final long number) {
    subscription.request(number);
  }

  @Override
  public void onComplete() {
    dispatch(
        () -> {
          if (!getError()) {
            stages.getFirst().thenRunAsync(() -> subscriber.onComplete());
          }
        });
  }

  @Override
  public void onNext(final T value) {
    if (value == null) {
      throw new NullPointerException("Can't emit null.");
    }

    dispatch(
        () -> {
          if (!getError()) {
            final CompletionStage previous = stages.getFirst();
            final CompletableFuture next = new CompletableFuture<>();

            stages.addFirst(next);

            previous
                .thenComposeAsync(v -> function.apply(value, v))
                .thenApply(
                    r ->
                        SideEffect.run(
                                () -> {
                                  subscriber.onNext(r);
                                  next.complete(r);
                                })
                            .andThenGet(() -> r))
                .exceptionally(
                    t -> {
                      subscriber.onError(t);
                      subscription.cancel();

                      return null;
                    });

            while (stages.size() > 10) {
              stages.removeLast();
            }
          }
        });
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy