拆分流,然后将流与RX合并

时间:2020-02-23 15:01:21

标签: concurrency rxjs rx-java rx-java2 system.reactive

我正在寻找某种最佳实践或模式来处理以下情况:

我想通过以下两种方式合并两个流:

                               |-------------|
                               | Transform A |------------>|---------|
              |---------|----->|-------------|             |         |
Stream A----->| Split A |                                  | Combine |-------> ?
              |---------|----->|--------------------|      |   All   |
                               | Transform and      |      |         |
                               | Combine A (latest) |----->|---------|
                               | and B (latest)     |
Stream B---------------------->|--------------------|

流A和B正在异步生成事件。我想结合A和B的最新信息,并将结果与​​A之上的计算结果结合起来。

当收到B中的事件时,整个管道将使用该事件中的值以及A中的最新值来运行。

是否有一种优雅的方法来确保在收到A中的事件时,全部合并与基于A中该事件的事件一起运行,并避免在转换A 转换并合并A和B

3 个答案:

答案 0 :(得分:1)

您可以通过将单线程调度程序与去抖动运算符组合在一起来实现:

cases: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Case' }],

这当然不是开箱即用的,您必须弄乱调度程序和测试以使其正确,但是也许这个想法会有所帮助。如果使用防抖为“零”的超时功能感觉有点太过分了,您还可以使用其他签名,以可观察的方式完全控制防抖周期。

但是,如果您的预期用途不是一般情况,并且仅针对上述情况,则可以使用类似以下的方法来简化问题:

class ManualExecutor : Executor {
    private val tasks = ArrayDeque<Runnable>()

    override fun execute(command: Runnable) = tasks.push(command)

    fun runAllTasks() {
        while (tasks.isNotEmpty()) {
            tasks.pop().run()
        }
    }
}

val a: Observable<A>
val b: Observable<B>

val scheduler = Schedulers.from(ManualExecutor())

val aTransformed = a.observeOn(scheduler).map { transformA(it) }
val aCombinedWithB = combine(a, b).observeOn(scheduler)

val final = combine(aTransformed, aCombinedWithB).debounce(0)

// some time later....
emitA() // now all the updates are queued in our ManualExecutor
scheduler.runAllTasks() // final will only emit once, not twice!

答案 1 :(得分:0)

使用Observable.Publish(source, x => ...)可确保您仅拥有source的一个订阅,但您可以将其用作变量x的任意次数。这样可以确保您永远不会在source上遇到任何竞赛情况。

您可以这样使用它:

Func<A, C> transform_A = a => ...;
Func<A, B, D> combine_A_and_B = (a, b) => ...;
Func<C, D, E> combine_all = (c, d) => ...;

IObservable<A> stream_a = ...;
IObservable<B> stream_b = ...;

IObservable<E> query =
    stream_a
        .Publish(stream_a_published =>
        {
            var stream_c = stream_a_published.Select(transform_A);
            var stream_d = stream_a_published.CombineLatest(stream_b, combine_A_and_B);
            return stream_c.CombineLatest(stream_d, combine_all);
        });

答案 2 :(得分:0)

基于@tonicsoft的最新评论,其想法是拥有这样的东西:

Flowable<Pair<Long, String>> streamPairA = Flowable.interval(3, SECONDS)
                                                   .doOnNext(l -> System.out.println("new value of A: " + l))
                                                   .map(l -> new Pair<>(l, "Hello " + l));
Flowable<Long> streamB = Flowable.interval(5, SECONDS)
                                 .doOnNext(l -> System.out.println("new value of B: " + l));

Flowable.combineLatest(streamPairA, streamB, (x, y) -> "--> " + x.value()
                                                        + " (latest A: " + x.key() + ", latest B: " + y + ")")
        .subscribe(System.out::println);

Flowable.timer(1, MINUTES) // Just to block the main thread for a while
        .blockingSubscribe();