ReactiveX在超时后发出null或sentinel值

时间:2016-03-08 16:52:29

标签: rx-java reactivex

在一段时间内没有发出项目后,寻找一种简洁的方法来转换源Observable以发出单个null(或哨兵值)。

例如,如果源observable发出1, 2, 3,则在发出4, 5, 6之前停止发射10秒钟我希望发出的项目为1, 2, 3, null, 4, 5, 6

用例用于在UI中显示值,如果最后一个值是陈旧/过时,显示的值应变为短划线-N/A

我查看了timeout运算符,但是当超时发生时它会终止Observable,这是不可取的。

使用RxJava。

2 个答案:

答案 0 :(得分:2)

基于akarnokd's answeran answer in a similar question,另一种实现方式:

单个标记值(根据OP)

如果您正在寻找一个值来表明排放之间的时间间隔:

final TestScheduler scheduler = new TestScheduler();
final TestSubject<Integer> subject = TestSubject.create(scheduler);
final TestSubscriber<Integer> subscriber = new TestSubscriber<>();

final long duration = 100;
final Observable<Integer> timeout = Observable.just(-1).delay(duration, TimeUnit.MILLISECONDS, scheduler)
    .concatWith(Observable.never())
    .takeUntil(subject)
    .repeat();

subject.mergeWith(timeout).subscribe(subscriber);

subject.onNext(1,   0);
subject.onNext(2, 100);
subject.onNext(3, 200);

scheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS);
scheduler.advanceTimeBy(300, TimeUnit.MILLISECONDS);

subject.onNext(4,   0);
subject.onNext(5, 100);
subject.onNext(6, 200);

scheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS);

subscriber.assertNoTerminalEvent();
subscriber.assertReceivedOnNext(Arrays.asList(1, 2, 3, -1, 4, 5, 6));

连续哨兵值

如果您希望在源观察源在一段时间内不发光后继续接收值:

final TestScheduler scheduler = new TestScheduler();
final TestSubject<Integer> subject = TestSubject.create(scheduler);
final TestSubscriber<Integer> subscriber = new TestSubscriber<>();

final long duration = 100;
final Observable<Integer> timeout = Observable.interval(duration, duration, TimeUnit.MILLISECONDS, scheduler)
    .map(x -> -1)
    .takeUntil(subject)
    .repeat();

subject.mergeWith(timeout).subscribe(subscriber);

subject.onNext(1,   0);
subject.onNext(2, 100);
subject.onNext(3, 200);

scheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS);
scheduler.advanceTimeBy(300, TimeUnit.MILLISECONDS);

subject.onNext(4,   0);
subject.onNext(5, 100);
subject.onNext(6, 200);

scheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS);

subscriber.assertNoTerminalEvent();
subscriber.assertReceivedOnNext(Arrays.asList(1, 2, 3, -1, -1, -1, 4, 5, 6));

差异是timeout可观察到的以及是否经常发生。

您可以根据需要将-1替换为null

使用Java 1.8.0_72使用RxJava 1.0.17测试上述所有内容。

答案 1 :(得分:1)

您可以通过稍微复杂的publish-amb-timer设置来实现此目的:

PublishSubject<Integer> ps = PublishSubject.create();
TestScheduler s = Schedulers.test();
TestSubscriber<Integer> ts = new TestSubscriber<>();

ps.publish(o -> 
    o.take(1).ambWith(Observable.timer(10, TimeUnit.SECONDS, s).map(v -> (Integer)null))
    .repeat().takeUntil(o.ignoreElements())
).subscribe(ts);

ps.onNext(1);
ps.onNext(2);
ps.onNext(3);

s.advanceTimeBy(15, TimeUnit.SECONDS);

ps.onNext(4);
ps.onNext(5);
ps.onNext(6);
ps.onCompleted();

ts.assertValues(1, 2, 3, null, 4, 5, 6);

发生的消息是源已发布,因此您可以从中逐个获取项目或计时器事件,确保最快的获胜并使用下一个值重复它,所有这些都不会一直重新订阅原始来源

编辑修复了上游完成后重复()进入无限循环的情况。