结合多个Observable时如何急切地返回RxJava?

时间:2016-08-29 14:05:34

标签: android rx-java

说我有3个条件要检查。在命令式编程中,我会写

boolean foo(A a, B b, C c){
  if(a.meetsCriteria()){ return true; }
  if(b.meetsCriteria()){ return true; }
  if(c.meetsCriteria()){ return true; }
  return false;
}

这是我将上述内容转换为被动

的愚蠢行为
Observable<Boolean> foo(A a, B b, C c){
  Observable.zip(
    a.meetsCriteria(),
    b.meetsCriteria(),
    c.meetsCriteria(),
    (bool1, bool2, bool3)->{return bool1 | bool2 | bool3;}
}

这里的问题是调用了所有3个meetsCriteria()方法,因此这种实现不会急切地返回。我希望B和C&#39; meetsCriteria()A.meetsCriteria()返回true时不执行。正确的反应转换是什么?

2 个答案:

答案 0 :(得分:0)

你只需要另一个操作员。而不是Observable.zip,您可以使用一系列flatMaps:

a.meetsCriteria().flatMap(
    resa -> resa ? Observable.just(true) : b.meetsCriteria().flatMap(
         resb -> resb ? Observable.just(true) : c.meetsCriteria()
    )
) 

然而,这基本上会使代码串行在最坏的情况下(所有三个标准都是假的)。

因此,要从反应式编程中受益,您必须立即运行所有这些。我提出了以下想法:

  1. merge观察者(他们将并行运行)
  2. filter其中任何一个发出的true个值
  3. 使用defaultIfEmpty(false)运算符,这是所有可观测量发出的情况的后备false
  4. 最后,只需使用first(),这样我们就可以得到一个布尔值。
  5. 你可以自己试试:

    Observable<Boolean> a = Observable.just(false).delay(15, TimeUnit.SECONDS);
    Observable<Boolean> b = Observable.just(true).delay(5, TimeUnit.SECONDS);
    Observable<Boolean> c = Observable.just(false).delay(50, TimeUnit.DAYS);
    
    final Observable<Boolean> res = Observable.merge(a, b, c)
                .filter(item -> item == true)
                .defaultIfEmpty(false)
                .first();
    
    System.err.println(res.toBlocking().first());
    

    在我的机器上运行大约5秒,这正是可观察b需要发出true的时间。但是,如果你这样做:

    Observable<Boolean> b = Observable.just(false).delay(5, TimeUnit.SECONDS);
    

    ,你注定要等待50天,观察c完成:)嗯,显然无法知道c是否会为合并运算符发出任何内容,所以这是不可避免的。

    当然,如果你尝试,你仍然可以从并行性中受益:

    Observable<Boolean> a = Observable.just(false).delay(10, TimeUnit.SECONDS);
    Observable<Boolean> b = Observable.just(false).delay(10, TimeUnit.SECONDS);
    Observable<Boolean> c = Observable.just(false).delay(10, TimeUnit.SECONDS);
    

    ,总运行时间约为10秒,而不是串行版本的30秒。

    P.S。也提出了另一种解决方案,我觉得它更自然。希望代码不言自明:

    final Observable<Boolean> res = Observable.combineLatest(
        a.startWith((Boolean)null),
        b.startWith((Boolean)null),
        c.startWith((Boolean)null),
        (Boolean b1, Boolean b2, Boolean b3) -> {
            // note that b1/b2/b3 might be null
            // we interpret that as 'unfinished' computation
            if (Boolean.TRUE.equals(b1) || Boolean.TRUE.equals(b2) || Boolean.TRUE.equals(b3)) {
                // if any of source observables has finished and was true, the result is obviously true
                return true;            }
            if (Boolean.FALSE.equals(b1) && Boolean.FALSE.equals(b2) && Boolean.FALSE.equals(b3)) {
                // if all of source observables have finished and are false, the result is false
                return false;
            }
            return null; // otherwise we're in some kind of intermediate state
        }
    )
        .filter(item -> item != null)
        .first();
    

答案 1 :(得分:0)

嗯......怎么样:

return Observable.concatMap(
          a.meetsCriteria(),
          b.meetsCriteria(),
          c.meetsCriteria())
      .filter(value -> value)
      .takeFirst();