RxJs staircase when chaining Observable

与世无争的帅哥 提交于 2019-12-10 18:40:13

问题


Before I was using Promise with async/await syntax in Typescript looking as following

const fooData = await AsyncFooData();
const barData = await AsyncBarData();

... do something with fooData and barData

If I do it with RxJs Observable<T> it becomes something like this for me

AsyncFooData().subscribe(fooData => {
   AsyncBarData().subscribe(barData => {
      ... do something with fooData and barData
   })
})

Is there some better way to do this? Because it becomes fast not readable, ala Staircase, if I would have more AsyncData I need to work with.


回答1:


You won't quite be able to do what you've done with async/await since you still have to have the .subscribe callback and there is the potential for multiple properties to be emitted. You should never nest .subscribe calls. Typically you will use a higher order Observable operator such as mergeMap, or an Observable creator that syncs the emission of multiple Observables together such as combineLatest:

combineLatest(AsyncFooData(), AsyncBarData()).subscribe([fooData, barData] => {

});

The exact function you need depends on your own needs as well as how foo and bar emit:

  • combineLatest - Emits every time any source emits (note: does not start emitting until all sources have emitted once).
  • zip - Synchronizes emissions, e.g. emits once each Observable has emitted once, then twice, etc.
  • forkJoin - Emits when all source observables have completed
  • merge - Emits whenever any source emits. It does not combine outputs like the others above.

There are more available: https://www.learnrxjs.io/operators/combination/




回答2:


I think you are trying to find a nice way to chain multiple async operations in a 'cleaner' way. This is what I do:

  • i used from() as I assumed AsyncFooData returns a promise. If it returns Observable, just remove the from().
  • avoid multiple subscriptions (usually, less frequent than we think).
  • use pipe() to chain appropriate operators, as many as you need in a pretty flat way.
  • subscribe() is called when all the operations are done.
  • Style A pass along result from foo all the way to subscribe's next function.
  • Style B only works with the last async operation's result.

Note: These examples are written to share concept/approach, without IDE syntax check. The concept should work but apology if there are syntax errors.

// A: if you need both foo and bar
from(AsyncFooData()).pipe(
  concatMap(foo => AsyncBarData().pipe(
    map(bar => ({foo, bar})
  )),
  tap(val => console.log(val), // chain more operators here...
).subscribe(({foo, bar}) => {
  // do stuff with foo and bar
})

// B: if foo is only used to get bar (i.e. no need to pass it along)
from(AsyncFooData()).pipe(
  concatMap(foo => AsyncBarData(foo)), // assume foo is only used to get bar
  tap(val => console.log(val), // chain more operators here...
).subscribe(bar => {
  // do stuff with bar
})



回答3:


You may zip and get fooData and barData and do whatever you want to do.

zip(AsyncFooData(), AsyncBarData()).subscribe([fooData, barData]) => {})

Here zip is taken as an example. You may use other operators such as combineLatest according to your needs.

I refrain from explaining the differences between zip and combineLatest and other operators here because doing so may clutter this answer. Instead I point the following resources, there things are clearly explained with diagrams and examples:

(1) Official doc

(2) Marble diagrams



来源:https://stackoverflow.com/questions/51065887/rxjs-staircase-when-chaining-observable

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!