我有一个Observable源和一个操作符链,可以将源转换为目标类型。通常,对于每个源项目,最多会生成一个目标。
Source -> Operator chain -> Target
运算符逻辑有点复杂,涉及使用IO调度程序进行多个异步数据库调用。我在这里省略了细节,因为它似乎没有相关性。 我看到的是新的Observables来自Source,而之前的Observable仍在被链条处理。所以它类似于某种管道。在许多情况下这可能是一件好事,但在我的情况下却不是。
所以我正在研究如何延迟源项目进入链(有效锁定它),直到前一个项目到达目标。是否有任何已知的模式?
我看到的一个丑陋的解决方案是在链的开头使用类似的东西:
zip(source, signal, (source, signal)->source)
其中signal是一个自定义observable,用于在每次链准备好接受新的源项目时将通知推送到(最初一个通知以及正在处理的项目到达链的末尾时) 但我发现它有点hacky。它可以更优雅地实现还是使用一组标准运算符?
以下是重现我不想要的行为的合成示例。 源是100ms间隔计时器。 运算符链是一个缓慢的(比源缓慢10倍)异步调用,它在Schedulers.io()上计算一个平方 目标项目实际上是一个源平方。
Subscription s = Observable.timer(100, 100, TimeUnit.MILLISECONDS)
.doOnNext(source->System.out.println("source: " + source))
.concatMap(source->Observable.create(subscr->{
Schedulers.io().createWorker().schedule(()->{
subscr.onNext(source * source);
subscr.onCompleted();
}, 1000, TimeUnit.MILLISECONDS);
}))
.doOnNext(target->System.out.println("target: " + target))
.subscribe();
Thread.sleep(10000);
s.unsubscribe();
打印出来源和目标:
source: 0
source: 1
source: 2
source: 3
source: 4
source: 5
source: 6
source: 7
source: 8
source: 9
source: 10
source: 11
target: 0
source: 12
source: 13
source: 14
source: 15
source: 16
source: 17
source: 18
source: 19
source: 20
target: 1
source: 21
source: 22
source: 23
source: 24
source: 25
source: 26
source: 27
source: 28
source: 29
source: 30
source: 31
target: 4
source: 32
source: 33
但我想要达到的目的是:
source: 0
target: 0
source: 1
target: 1
source: 2
target: 4
...
答案 0 :(得分:2)
根据您的来源类型,可以通过flatMap
参数化来实现maxConcurrency = 1
:
Observable.interval(100, 100, TimeUnit.MILLISECONDS)
.onBackpressureBuffer()
.doOnNext(source -> System.out.println("source: " + source))
.flatMap(source ->
Observable.just(source)
.map(v -> v * v)
.delay(1, TimeUnit.SECONDS), 1)
.doOnNext(target->System.out.println("target: " + target))
.subscribe();
Thread.sleep(10000);
此解决方案涉及缓冲,但如果源热,您可能需要选择不同的背压策略。
与要求并不严格相关,但我想指出你的这种模式:
Schedulers.io().createWorker().schedule(()->{
subscr.onNext(source * source);
subscr.onCompleted();
}, 1000, TimeUnit.MILLISECONDS);
泄漏了worker,并将使用不可重用的线程填满你的系统。如果你真的想通过˙Worker`延迟事件,你应该捕获并取消订阅工人实例:
Scheduler.Worker w = Schedulers.io().createWorker();
subscr.add(w);
w.schedule(() -> {
try {
subscr.onNext(source * source);
subscr.onCompleted();
} finally {
w.unsubscribe();
}
}, 1000, TimeUnit.MILLISECONDS);