Skip to content

Commit 22fc397

Browse files
Merge pull request #734 from akarnokd/DelayViaObservable
Delay with subscription and item delaying observables.
2 parents 8a29a64 + 337efeb commit 22fc397

File tree

3 files changed

+555
-0
lines changed

3 files changed

+555
-0
lines changed

rxjava-core/src/main/java/rx/Observable.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,6 +2291,41 @@ public static Observable<Long> timer(long initialDelay, long period, TimeUnit un
22912291
return create(new OperationTimer.TimerPeriodically(initialDelay, period, unit, scheduler));
22922292
}
22932293

2294+
/**
2295+
* Create an Observable which delays the events via another Observable on a per item-basis.
2296+
* <p>
2297+
* Note: onError event is immediately propagated.
2298+
*
2299+
* @param <U> the item delay value type (ignored)
2300+
* @param itemDelay function that returns an Observable for each source item which is
2301+
* then used for delaying that particular item until the Observable
2302+
* fires its first onNext event.
2303+
* @return an Observable which delays the events via another Observable on a per item-basis.
2304+
*/
2305+
public <U> Observable<T> delay(Func1<? super T, ? extends Observable<U>> itemDelay) {
2306+
return create(OperationDelay.delay(this, itemDelay));
2307+
}
2308+
/**
2309+
* Create an Observable which delays the subscription and events via another Observables on a per item-basis.
2310+
* <p>
2311+
* Note: onError event is immediately propagated.
2312+
*
2313+
* @param <U> the subscription delay value type (ignored)
2314+
* @param <V> the item delay value type (ignored)
2315+
* @param subscriptionDelay function that returns an Observable which will trigger
2316+
* the subscription to the source observable once it fires an
2317+
* onNext event.
2318+
* @param itemDelay function that returns an Observable for each source item which is
2319+
* then used for delaying that particular item until the Observable
2320+
* fires its first onNext event.
2321+
* @return an Observable which delays the events via another Observable on a per item-basis.
2322+
*/
2323+
public <U, V> Observable<T> delay(
2324+
Func0<? extends Observable<U>> subscriptionDelay,
2325+
Func1<? super T, ? extends Observable<V>> itemDelay) {
2326+
return create(OperationDelay.delay(this, subscriptionDelay, itemDelay));
2327+
}
2328+
22942329
/**
22952330
* Returns an Observable that emits the items emitted by the source
22962331
* Observable shifted forward in time by a specified delay. Error

rxjava-core/src/main/java/rx/operators/OperationDelay.java

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
import rx.Scheduler;
2424
import rx.Subscription;
2525
import rx.observables.ConnectableObservable;
26+
import rx.subscriptions.CompositeSubscription;
2627
import rx.subscriptions.SerialSubscription;
28+
import rx.subscriptions.Subscriptions;
2729
import rx.util.functions.Action0;
30+
import rx.util.functions.Func0;
2831
import rx.util.functions.Func1;
2932

3033
public final class OperationDelay {
@@ -82,4 +85,213 @@ public void call() {
8285
return ssub;
8386
}
8487
}
88+
/**
89+
* Delay the emission of the source items by a per-item observable that fires its first element.
90+
*/
91+
public static <T, U> OnSubscribeFunc<T> delay(Observable<? extends T> source,
92+
Func1<? super T, ? extends Observable<U>> itemDelay) {
93+
return new DelayViaObservable<T, Object, U>(source, null, itemDelay);
94+
}
95+
/**
96+
* Delay the subscription and emission of the source items by a per-item observable that fires its first element.
97+
*/
98+
public static <T, U, V> OnSubscribeFunc<T> delay(Observable<? extends T> source,
99+
Func0<? extends Observable<U>> subscriptionDelay,
100+
Func1<? super T, ? extends Observable<V>> itemDelay) {
101+
return new DelayViaObservable<T, U, V>(source, subscriptionDelay, itemDelay);
102+
}
103+
/**
104+
* Delay the emission of the source items by a per-item observable that fires its first element.
105+
*/
106+
private static final class DelayViaObservable<T, U, V> implements OnSubscribeFunc<T> {
107+
final Observable<? extends T> source;
108+
final Func0<? extends Observable<U>> subscriptionDelay;
109+
final Func1<? super T, ? extends Observable<V>> itemDelay;
110+
111+
public DelayViaObservable(Observable<? extends T> source,
112+
Func0<? extends Observable<U>> subscriptionDelay,
113+
Func1<? super T, ? extends Observable<V>> itemDelay) {
114+
this.source = source;
115+
this.subscriptionDelay = subscriptionDelay;
116+
this.itemDelay = itemDelay;
117+
}
118+
119+
@Override
120+
public Subscription onSubscribe(Observer<? super T> t1) {
121+
CompositeSubscription csub = new CompositeSubscription();
122+
123+
SerialSubscription sosub = new SerialSubscription();
124+
csub.add(sosub);
125+
SourceObserver<T, V> so = new SourceObserver<T, V>(t1, itemDelay, csub, sosub);
126+
if (subscriptionDelay == null) {
127+
sosub.set(source.subscribe(so));
128+
} else {
129+
Observable<U> subscriptionSource;
130+
try {
131+
subscriptionSource = subscriptionDelay.call();
132+
} catch (Throwable t) {
133+
t1.onError(t);
134+
return Subscriptions.empty();
135+
}
136+
SerialSubscription ssub = new SerialSubscription();
137+
csub.add(ssub);
138+
ssub.set(subscriptionSource.subscribe(new SubscribeDelay<T, U, V>(source, so, csub, ssub)));
139+
}
140+
141+
return csub;
142+
}
143+
/** Subscribe delay observer. */
144+
private static final class SubscribeDelay<T, U, V> implements Observer<U> {
145+
final Observable<? extends T> source;
146+
final SourceObserver<T, V> so;
147+
final CompositeSubscription csub;
148+
final Subscription self;
149+
/** Prevent any onError once the first item was delivered. */
150+
boolean subscribed;
151+
152+
public SubscribeDelay(
153+
Observable<? extends T> source,
154+
SourceObserver<T, V> so,
155+
CompositeSubscription csub, Subscription self) {
156+
this.source = source;
157+
this.so = so;
158+
this.csub = csub;
159+
this.self = self;
160+
}
161+
162+
@Override
163+
public void onNext(U args) {
164+
onCompleted();
165+
}
166+
167+
@Override
168+
public void onError(Throwable e) {
169+
if (!subscribed) {
170+
so.observer.onError(e);
171+
csub.unsubscribe();
172+
}
173+
}
174+
175+
@Override
176+
public void onCompleted() {
177+
subscribed = true;
178+
csub.remove(self);
179+
so.self.set(source.subscribe(so));
180+
}
181+
}
182+
/** The source observer. */
183+
private static final class SourceObserver<T, U> implements Observer<T> {
184+
final Observer<? super T> observer;
185+
final Func1<? super T, ? extends Observable<U>> itemDelay;
186+
final CompositeSubscription csub;
187+
final SerialSubscription self;
188+
/** Guard to avoid overlapping events from the various sources. */
189+
final Object guard;
190+
boolean done;
191+
int wip;
192+
193+
public SourceObserver(Observer<? super T> observer,
194+
Func1<? super T, ? extends Observable<U>> itemDelay,
195+
CompositeSubscription csub,
196+
SerialSubscription self) {
197+
this.observer = observer;
198+
this.itemDelay = itemDelay;
199+
this.csub = csub;
200+
this.guard = new Object();
201+
this.self = self;
202+
}
203+
204+
@Override
205+
public void onNext(T args) {
206+
Observable<U> delayer;
207+
try {
208+
delayer = itemDelay.call(args);
209+
} catch (Throwable t) {
210+
onError(t);
211+
return;
212+
}
213+
214+
synchronized (guard) {
215+
wip++;
216+
}
217+
218+
SerialSubscription ssub = new SerialSubscription();
219+
csub.add(ssub);
220+
ssub.set(delayer.subscribe(new DelayObserver<T, U>(args, this, ssub)));
221+
}
222+
223+
@Override
224+
public void onError(Throwable e) {
225+
synchronized (guard) {
226+
observer.onError(e);
227+
}
228+
csub.unsubscribe();
229+
}
230+
231+
@Override
232+
public void onCompleted() {
233+
boolean b;
234+
synchronized (guard) {
235+
done = true;
236+
b = checkDone();
237+
}
238+
if (b) {
239+
csub.unsubscribe();
240+
} else {
241+
self.unsubscribe();
242+
}
243+
}
244+
245+
void emit(T value, Subscription token) {
246+
boolean b;
247+
synchronized (guard) {
248+
observer.onNext(value);
249+
wip--;
250+
b = checkDone();
251+
}
252+
if (b) {
253+
csub.unsubscribe();
254+
} else {
255+
csub.remove(token);
256+
}
257+
}
258+
boolean checkDone() {
259+
if (done && wip == 0) {
260+
observer.onCompleted();
261+
return true;
262+
}
263+
return false;
264+
}
265+
}
266+
/**
267+
* Delay observer.
268+
*/
269+
private static final class DelayObserver<T, U> implements Observer<U> {
270+
final T value;
271+
final SourceObserver<T, U> parent;
272+
final Subscription token;
273+
274+
public DelayObserver(T value, SourceObserver<T, U> parent, Subscription token) {
275+
this.value = value;
276+
this.parent = parent;
277+
this.token = token;
278+
}
279+
280+
@Override
281+
public void onNext(U args) {
282+
parent.emit(value, token);
283+
}
284+
285+
@Override
286+
public void onError(Throwable e) {
287+
parent.onError(e);
288+
}
289+
290+
@Override
291+
public void onCompleted() {
292+
parent.emit(value, token);
293+
}
294+
295+
}
296+
}
85297
}

0 commit comments

Comments
 (0)