延迟物品排放,直到物品从另一个观察者发出

时间:2015-05-14 00:23:33

标签: reactive-programming rx-java

现在玩RxJava并偶然发现以下问题:

我有两个不同的流:

  • 使用商品流
  • Stream(只有1个项目),它会发出第一个流的转换信息。

所以基本上我有项目流,我希望所有这些项目都与第二个流中的单个项目相结合:

---- ---- A1 A2 A3 ---- ---- ---- A4 A5 ---- | --------------->

------------- b1-- | ------------------------------ ----->

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

------------ A1B1-A2B1-A3B1-A4B1-a5b1 -------->

它看起来与combileLatest运算符非常相似,但combineLatest将忽略第一个流中的所有项目,除了最接近第二个流中的项目。这意味着我不会收到a1b1 - 发出的第一个结果项目将是a2b1

我还查看了delay运算符,但它不允许我指定close流,就像使用buffer运算符一样

是否有任何花哨的操作员可以解决上述问题?

2 个答案:

答案 0 :(得分:2)

有几种方法可以实现这一目标:

1)flatMap超过b,如果您不需要提前a

b.flatMap(bv -> a.map(av -> together(av, bv)));

2)当然,您可以cache,但它会在整个广告播放期间保留您的a

3)使用groupBy有点非常规,因为它的GroupedObservable缓存值直到单个订阅者到达,重放缓存的值并继续作为常规直接可观察(让所有先前的缓存值继续)。

Observable<Long> source = Observable.timer(1000, 1000, TimeUnit.MILLISECONDS)
        .doOnNext(v -> System.out.println("Tick"))
        .take(10);
Observable<String> other = Observable.just("-b").delay(5000, TimeUnit.MILLISECONDS)
        .doOnNext(v -> System.out.println("Tack"))
        ;

source.groupBy(v -> 1)
.flatMap(g -> 
    other.flatMap(b -> g.map(a -> a + b))
).toBlocking().forEach(System.out::println);

它的工作原理如下:

  • 通过将所有内容从源分组到第1组来抓住GroupedObservable
  • 当小组g到达时,我们开始观察&#39;另一个可观察的。
  • 一旦其他人发射了它的元素,我们就把它拿出来并将它映射到整个组中,并开始观察&#39;它也带来了a + b s。
  • 的最终序列

我添加了doOnNexts,因此您可以在other触发&#34; Tack&#34;之前看到源是真正有效的。

答案 1 :(得分:1)

AFAIK,没有内置的操作员来实现您所描述的行为。您始终可以实现自定义运算符或在现有运算符之上构建它。我认为第二个选项更容易实现,这是代码:

public static <L, R, T> Observable<T> zipper(final Observable<? extends L> left, final Observable<? extends R> right, final Func2<? super L, ? super R, ? extends T> function) {
    return Observable.defer(new Func0<Observable<T>>() {
        @Override
        public Observable<T> call() {
            final SerialSubscription subscription = new SerialSubscription();
            final ConnectableObservable<? extends R> cached = right.replay();

            return left.flatMap(new Func1<L, Observable<T>>() {
                @Override
                public Observable<T> call(final L valueLeft) {
                    return cached.map(new Func1<R, T>() {
                        @Override
                        public T call(final R valueRight) {
                            return function.call(valueLeft, valueRight);
                        }
                    });
                }
            }).doOnSubscribe(new Action0() {
                @Override
                public void call() {
                    subscription.set(cached.connect());
                }
            }).doOnUnsubscribe(new Action0() {
                @Override
                public void call() {
                    subscription.unsubscribe();
                }
            });
        }
    });
}

如果您对代码有任何疑问,我可以详细解释。

<强>更新

关于如何解决我的解决方案与以下方案的不同之处:

left.flatMap(valueLeft -> right.map(valueRight -> together(valueLeft, valueRight)));
  1. 并行执行 - 在我的实现中,leftright可观察对象并行执行。 right可观察的left不必等待right个人发出第一个项目。
  2. 缓存 - 我的解决方案只为b1观察者订阅一次并缓存其结果。这就是为什么aXXX对于所有right项始终相同的原因。每次left发出项目时,akarnokd提供的解决方案都会订阅b1观察点。这意味着:

    • 无法保证b不会更改其值。例如,对于以下可观察对象,您将为每个a获得不同的final Observable<Double> right = Observable.defer(new Func0<Observable<Double>>() { @Override public Observable<Double> call() { return Observable.just(Math.random()); } });

      right
    • 如果left observable是一项耗时的操作(例如网络呼叫),则每次Hashtable props = new Hashtable(); props.put(Context.SECURITY_PRINCIPAL, "cn=Bob,cn=Users,dc=myCompany,dc=com"); props.put(Context.SECURITY_CREDENTIALS, "Password1"); props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); props.put(Context.PROVIDER_URL, url); InitialLdapContext context = new InitialLdapContext(props, null); observable发出新项目时,您都必须等待其完成。