1313
1414package io .reactivex .rxjava4 .core ;
1515
16+ import java .lang .reflect .InvocationTargetException ;
17+ import java .util .Objects ;
18+ import java .util .concurrent .*;
19+
1620import io .reactivex .rxjava4 .annotations .NonNull ;
1721import io .reactivex .rxjava4 .disposables .*;
22+ import io .reactivex .rxjava4 .exceptions .Exceptions ;
23+ import io .reactivex .rxjava4 .functions .Consumer ;
24+ import io .reactivex .rxjava4 .internal .operators .streamable .*;
1825
1926/**
2027 * The {@code IAsyncEnumerable} of the Java world.
@@ -41,4 +48,105 @@ public abstract class Streamable<@NonNull T> {
4148 public final Streamer <T > stream () {
4249 return stream (new CompositeDisposable ()); // FIXME, use a practically no-op disposable container instead
4350 }
51+
52+ /**
53+ * Returns an empty {@code Streamable} that never produces an item and just completes.
54+ * @param <T> the element type
55+ * @return the {@code Streamable} instance
56+ */
57+ @ NonNull
58+ public static <@ NonNull T > Streamable <T > empty () {
59+ return new StreamableEmpty <>();
60+ }
61+
62+ /**
63+ * Returns a single-element {@code Streamable} that produces the constant item and completes.
64+ * @param <T> the element type
65+ * @param item the constant item to produce
66+ * @return the {@code Streamable} instance
67+ */
68+ public static <@ NonNull T > Streamable <T > just (@ NonNull T item ) {
69+ Objects .requireNonNull (item , "item is null" );
70+ return new StreamableJust <>(item );
71+ }
72+
73+ /**
74+ * Consumes elements from this {@code Streamable} via the provided executor service.
75+ * @param consumer the callback that gets the elements until completion
76+ * @return a Disposable that let's one cancel the sequence asynchronously.
77+ */
78+ @ NonNull
79+ public final CompletionStageDisposable <Void > forEach (@ NonNull Consumer <? super T > consumer ) {
80+ CompositeDisposable canceller = new CompositeDisposable ();
81+ return forEach (consumer , canceller , Executors .newVirtualThreadPerTaskExecutor ());
82+ }
83+
84+ /**
85+ * Consumes elements from this {@code Streamable} via the provided executor service.
86+ * @param consumer the callback that gets the elements until completion
87+ * @param canceller the container to trigger cancellation of the sequence
88+ * @return the {@code CompletionStage} that gets notified when the sequence ends
89+ */
90+ public final CompletionStageDisposable <Void > forEach (@ NonNull Consumer <? super T > consumer , @ NonNull DisposableContainer canceller ) {
91+ return forEach (consumer , canceller , Executors .newVirtualThreadPerTaskExecutor ());
92+ }
93+
94+ /**
95+ * Consumes elements from this {@code Streamable} via the provided executor service.
96+ * @param consumer the callback that gets the elements until completion
97+ * @param executor the service that hosts the blocking waits.
98+ * @return a Disposable that let's one cancel the sequence asynchronously.
99+ */
100+ @ NonNull
101+ public final CompletionStageDisposable <Void > forEach (@ NonNull Consumer <? super T > consumer , @ NonNull ExecutorService executor ) {
102+ CompositeDisposable canceller = new CompositeDisposable ();
103+ return forEach (consumer , canceller , executor );
104+ }
105+
106+ /**
107+ * Consumes elements from this {@code Streamable} via the provided executor service.
108+ * @param consumer the callback that gets the elements until completion
109+ * @param canceller the container to trigger cancellation of the sequence
110+ * @param executor the service that hosts the blocking waits.
111+ * @return the {@code CompletionStage} that gets notified when the sequence ends
112+ */
113+ @ SuppressWarnings ("unchecked" )
114+ public final CompletionStageDisposable <Void > forEach (@ NonNull Consumer <? super T > consumer , @ NonNull DisposableContainer canceller , @ NonNull ExecutorService executor ) {
115+ Objects .requireNonNull (consumer , "consumer is null" );
116+ Objects .requireNonNull (canceller , "canceller is null" );
117+ Objects .requireNonNull (executor , "executor is null" );
118+ final Streamable <T > me = this ;
119+ var future = executor .submit (() -> {
120+ try (var str = me .stream (canceller )) {
121+ while (!canceller .isDisposed ()) {
122+ if (str .next ().toCompletableFuture ().join ()) {
123+ consumer .accept (Objects .requireNonNull (str .current (), "The upstream Streamable " + me .getClass () + " produced a null element!" ));
124+ } else {
125+ break ;
126+ }
127+ }
128+ } catch (final Throwable crash ) {
129+ Exceptions .throwIfFatal (crash );
130+ if (crash instanceof RuntimeException ex ) {
131+ throw ex ;
132+ }
133+ if (crash instanceof Exception ex ) {
134+ throw ex ;
135+ }
136+ throw new InvocationTargetException (crash );
137+ }
138+ return null ;
139+ });
140+ canceller .add (Disposable .fromFuture (future ));
141+ return new CompletionStageDisposable <Void >(StreamableHelper .toCompletionStage ((Future <Void >)(Future <?>)future ), canceller );
142+ }
143+
144+ /**
145+ * Consume this {@code Streamable} via the given flow-reactive-streams subscriber.
146+ * @param subscriber the subscriber to consume with.
147+ * @param executor the service that hosts the blocking waits.
148+ */
149+ public final void subscribe (@ NonNull Flow .Subscriber <? super T > subscriber , @ NonNull ExecutorService executor ) {
150+
151+ }
44152}
0 commit comments