简体   繁体   English

RxJava:如何用依赖关系组合多个Observable并在最后收集所有结果?

[英]RxJava: how to compose multiple Observables with dependencies and collect all results at the end?

I'm learning RxJava and, as my first experiment, trying to rewrite the code in the first run() method in this code (cited on Netflix's blog as a problem RxJava can help solve) to improve its asynchronicity using RxJava, ie so it doesn't wait for the result of the first Future ( f1.get() ) before proceeding on to the rest of the code. 我正在学习RxJava,并且,作为我的第一个实验,尝试在此代码中重写第一个run()方法中的代码 (在Netflix的博客中引用,作为RxJava可以帮助解决的问题),以使用RxJava改善其异步性,即所以它在继续执行其余代码之前,不会等待第一个Future( f1.get() )的结果。

f3 depends on f1 . f3取决于f1 I see how to handle this, flatMap seems to do the trick: 我看到如何处理这个问题, flatMap似乎可以解决这个问题:

Observable<String> f3Observable = Observable.from(executor.submit(new CallToRemoteServiceA()))
    .flatMap(new Func1<String, Observable<String>>() {
        @Override
        public Observable<String> call(String s) {
            return Observable.from(executor.submit(new CallToRemoteServiceC(s)));
        }
    });

Next, f4 and f5 depend on f2 . 接下来, f4f5取决于f2 I have this: 我有这个:

final Observable<Integer> f4And5Observable = Observable.from(executor.submit(new CallToRemoteServiceB()))
    .flatMap(new Func1<Integer, Observable<Integer>>() {
        @Override
        public Observable<Integer> call(Integer i) {
            Observable<Integer> f4Observable = Observable.from(executor.submit(new CallToRemoteServiceD(i)));
            Observable<Integer> f5Observable = Observable.from(executor.submit(new CallToRemoteServiceE(i)));
            return Observable.merge(f4Observable, f5Observable);
        }
    });

Which starts to get weird ( merge ing them probably isn't what I want...) but allows me to do this at the end, not quite what I want: 哪个开始变得怪异( merge它们可能不是我想要的......)但是允许我最后这样做,而不是我想要的:

f3Observable.subscribe(new Action1<String>() {
    @Override
    public void call(String s) {
        System.out.println("Observed from f3: " + s);
        f4And5Observable.subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer i) {
                System.out.println("Observed from f4 and f5: " + i);
            }
        });
    }
});

That gives me: 这给了我:

Observed from f3: responseB_responseA
Observed from f4 and f5: 140
Observed from f4 and f5: 5100

which is all the numbers, but unfortunately I get the results in separate invocations, so I can't quite replace the final println in the original code: 这是所有数字,但不幸的是我在单独的调用中得到结果,所以我不能完全替换原始代码中的最终println:

System.out.println(f3.get() + " => " + (f4.get() * f5.get()));

I don't understand how to get access to both those return values on the same line. 我不明白如何在同一行上访问这两个返回值。 I think there's probably some functional programming fu I'm missing here. 我想可能有一些功能性编程我在这里缺少。 How can I do this? 我怎样才能做到这一点? Thanks. 谢谢。

It looks like all you really need is a bit more encouragement and perspective on how RX is used. 看起来你真正需要的是对如何使用RX的更多鼓励和观点。 I'd suggest you read more into the documentation as well as marble diagrams (I know they're not always useful). 我建议你阅读更多文档以及大理石图表(我知道它们并不总是有用)。 I also suggest looking into the lift() function and operators. 我还建议调查lift()函数和运算符。

  • The entire point of an observable is to concatenate data flow and data manipulation into a single object 可观察的整个点是将数据流和数据操作连接成单个对象
  • The point of calls to map , flatMap and filter are to manipulate the data in your data flow mapflatMapfilter的调用点是操纵数据流中的数据
  • The point of merges are to combine data flows 合并点是组合数据流
  • The point of operators are to allow you to disrupt a steady stream of observables and define your own operations on a data flow. 运算符的要点是允许您中断稳定的可观察流并在数据流上定义自己的操作。 For example, I coded a moving average operator. 例如,我编码了一个移动平均算子。 That sums up n double s in an Observable of doubles to return a stream of moving averages. 凝聚了n double S IN可观察到的双打回归移动平均流。 The code literally looked like this 代码字面上看起来像这样

    Observable movingAverage = Observable.from(mDoublesArray).lift(new MovingAverageOperator(frameSize))

You'll be a relieved that a lot of the filtering methods that you take for granted all have lift() under the hood. 你会感到欣慰的是,许多你认为理所当然的过滤方法都有lift()

With that said; 照这样说; all it takes to merge multiple dependencies is: 合并多个依赖项所需的全部是:

  • changing all incoming data to a standard data type using map or flatMap 使用mapflatMap将所有传入数据更改为标准数据类型
  • merging standard data-types to a stream 将标准数据类型合并到流中
  • using custom operators if one object needs to wait on another, or if you need to order data in the stream. 如果一个对象需要在另一个对象上等待,或者您需要在流中订购数据,则使用自定义运算符。 Caution: this approach will slow the stream down 注意:这种方法会减慢流的速度
  • using to list or subscribe to collect all of that data 用于列出或订阅以收集所有这些数据

Edit: someone converted the following text, which I had added as an edit on the question, into an answer, which I appreciate, and understand may be the proper SO thing to do, however I do not consider this an answer because it's clearly not the right way to do it . 编辑:有人将以下文本转换为答案,我将其作为问题的编辑添加到答案中,我很欣赏,并且理解可能是正确的SO要做的事情,但我不认为这是一个答案,因为它显然不是正确的方法 I would not ever use this code nor would I advise anyone to copy it. 我不会使用此代码,也不会建议任何人复制它。 Other/better solutions and comments welcome! 其他/更好的解决方案和评论欢迎!


I was able to solve this with the following. 我能用以下方法解决这个问题。 I didn't realize you could flatMap an observable more than once, I assumed results could only be consumed once. 我没有意识到你可以不止一次flatMap一个observable,我假设结果只能消耗一次。 So I just flatMap f2Observable twice (sorry, I renamed some stuff in the code since my original post), then zip on all the Observables, then subscribe to that. 所以我只是flatMap f2Observable两次(对不起,我在我原来的帖子中重命名代码中的一些东西),然后在所有的Observables上zip ,然后订阅。 That Map in the zip to aggregate the values is undesirable because of the type juggling. 由于类型杂乱,在zip Map以聚合值是不合需要的。 Other/better solutions and comments welcome! 其他/更好的解决方案和评论欢迎! The full code is viewable in a gist . 完整代码可以在要点中查看 Thank you. 谢谢。

Future<Integer> f2 = executor.submit(new CallToRemoteServiceB());
Observable<Integer> f2Observable = Observable.from(f2);
Observable<Integer> f4Observable = f2Observable
    .flatMap(new Func1<Integer, Observable<Integer>>() {
        @Override
        public Observable<Integer> call(Integer integer) {
            System.out.println("Observed from f2: " + integer);
            Future<Integer> f4 = executor.submit(new CallToRemoteServiceD(integer));
            return Observable.from(f4);
        }       
    });     

Observable<Integer> f5Observable = f2Observable
    .flatMap(new Func1<Integer, Observable<Integer>>() {
        @Override
        public Observable<Integer> call(Integer integer) {
            System.out.println("Observed from f2: " + integer);
            Future<Integer> f5 = executor.submit(new CallToRemoteServiceE(integer));
            return Observable.from(f5);
        }       
    });     

Observable.zip(f3Observable, f4Observable, f5Observable, new Func3<String, Integer, Integer, Map<String, String>>() {
    @Override
    public Map<String, String> call(String s, Integer integer, Integer integer2) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("f3", s);
        map.put("f4", String.valueOf(integer));
        map.put("f5", String.valueOf(integer2));
        return map;
    }       
}).subscribe(new Action1<Map<String, String>>() {
    @Override
    public void call(Map<String, String> map) {
        System.out.println(map.get("f3") + " => " + (Integer.valueOf(map.get("f4")) * Integer.valueOf(map.get("f5"))));
    }       
});     

And this yields me the desired output: 这会产生我想要的输出:

responseB_responseA => 714000

I think what you are looking for is switchmap. 我认为你要找的是switchmap。 We ran into a similar issue where we have a session service that handles getting a new session from an api, and we need that session before we can get more data. 我们遇到了类似的问题,我们有一个会话服务来处理从api获取新会话,我们需要该会话才能获得更多数据。 We can add to the session observable that returns the sessionToken for use in our data call. 我们可以添加会话observable,返回sessionToken以便在我们的数据调用中使用。

getSession returns an observable; getSession返回一个observable;

public getSession(): Observable<any>{
  if (this.sessionToken)
    return Observable.of(this.sessionToken);
  else if(this.sessionObservable)
    return this.sessionObservable;
  else {
    // simulate http call 
    this.sessionObservable = Observable.of(this.sessonTokenResponse)
    .map(res => {
      this.sessionObservable = null;
      return res.headers["X-Session-Token"];
    })
    .delay(500)
    .share();
    return this.sessionObservable;
  }
}

and getData takes that observable and appends to it. 并且getData获取该observable并附加到它。

public getData() {
  if (this.dataObservable)
    return this.dataObservable;
  else {
    this.dataObservable = this.sessionService.getSession()
      .switchMap((sessionToken:string, index:number) =>{
        //simulate data http call that needed sessionToken
          return Observable.of(this.dataResponse)
          .map(res => {
            this.dataObservable = null;
            return res.body;
          })
          .delay(1200)
        })
        .map ( data => {
          return data;
        })
        .catch(err => {
          console.log("err in data service", err);
         // return err;
        })
        .share();
    return this.dataObservable;
  }
}

You will still need a flatmap to combine the not dependent observables. 您仍然需要一个flatmap来组合不依赖的observable。

Plunkr: http://plnkr.co/edit/hiA1jP?p=info Plunkr: http ://plnkr.co/edit/hiA1jP?p = info

Where I got the idea to use switch map: http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html 我有想法使用切换地图: http//blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM