写点什么

5 张弹珠图彻底弄清 RxJS 的拉平策略:mergeMap、switchMap、concatMap、exhaustMap

作者:掘金安东尼
  • 2022 年 8 月 11 日
    中国台湾
  • 本文字数:1938 字

    阅读完需:约 6 分钟

5 张弹珠图彻底弄清 RxJS 的拉平策略:mergeMap、switchMap、concatMap、exhaustMap

RxJS 的操作符理解起来确实比较复杂,比如最常用的几种 map 操作符,本篇就来使劲冲一冲它们!!



  • 原创文章,非商业转载请说名出处


<hr>


map 操作想必大家一定不陌生:


const { of } = Rx;const { map  } = RxOperators;
const namesObservable = of('A', 'B');namesObservable.pipe( map(name => `map ${name}`))
namesObservable .subscribe(result => console.log(`${result}`))
// map A// map B
复制代码


很直观,因为 map 映射的是“值”,所以足够简单~


但是,如果说,map 映射的是 observable 呢 ?


const { of } = Rx;const { map } = RxOperators;
const namesObservable = of('A', 'B');
const http =(name)=>{ return of(`${name} 1`,`${name} 2`);}
namesObservable.pipe( map(name => http(name)))
namesObservable.subscribe(result => console.log(`${result}`))
// 则会得到两个 observable 对象// ****observable{ .. }// observable{ .. }
复制代码


我们在 https://rxviz.com/ 的弹珠图中,可以清晰的看到:返回的仍是 observable



并且 observable 由最初的 1 个,变成了 2 个(圆圈就是 observable),数据仍在里面没有被订阅解析出来。


虽然,我们可以用粗暴的方法,在订阅 .subscribe 里面再次调用订阅 .subscribe ,则可得值:


const { of } = Rx;const { map } = RxOperators;
const namesObservable = of('A', 'B');
const http =(name)=>{ return of(`${name} 1`,`${name} 2`);}
namesObservable.pipe( map(name => http(name)))
namesObservable .subscribe(resultObservable => { resultObservable.subscribe(result => console.log(`${result}`) )})
// A1// A2// B1// B2
复制代码


但是,这样包裹写法注定是不优雅的,所以,为了解决这个差异,RxJS 引入了 —— Flattening(扁平化)策略!!


我们可以借助 flatMap 操作符,则能得到同样的解析值的效果~


flatMap 其实也就是我们熟知的 mergeMap 操作符;


代码如下:


const { of } = Rx;const { mergeMap} = RxOperators;
const namesObservable = of('A', 'B');
const http =(name)=>{ return of(`${name} 1`,`${name} 2`);}
namesObservable.pipe( mergeMap(name => http(name)))
namesObservable.subscribe(result => console.log(`${result}`))// A1// A2// B1// B2
复制代码


更进一步,沿着这种偏平化策略的思路,除了 mergeMap,RxJS 又引入了 switchMap、concatMap 和 exhaustMap,它们能够提供不同方向的拉平策略。


我们再借助 https://rxviz.com/ 的弹珠图,一眼便能看到它们的差异:


设置一个定时器,每一秒都发出一个 observable,一共发 3 次,来看下分别得值;


  • mergeMap


const { of,interval} = Rx;const { mergeMap,take,map } = RxOperators;
const namesObservable = of('A', 'B');
const http =(name)=>{ return interval(1000) .pipe( take(3), map(()=>of(`${name} 1`,`${name} 2`)) )}
namesObservable.pipe( mergeMap(name => http(name)))
复制代码



mergeMap 会同时维护多个活动的内部订阅;


  • switchMap


const { of,interval} = Rx;const { switchMap,take,map } = RxOperators;
const namesObservable = of('A', 'B');
const http =(name)=>{ return interval(1000) .pipe( take(3), map(()=>of(`${name} 1`,`${name} 2`)) )}
namesObservable.pipe( switchMap(name => http(name)))
复制代码



switchMap,在每次发出时,会取消前一个内部 observable 的订阅,然后订阅一个新的 observable;


  • concatMap


const { of,interval} = Rx;const { concatMap ,take,map } = RxOperators;
const namesObservable = of('A', 'B');
const http =(name)=>{ return interval(1000) .pipe( take(3), map(()=>of(`${name} 1`,`${name} 2`)) )}
namesObservable.pipe( concatMap (name => http(name)))
复制代码



concatMap 会在之前前一个内部 observable 完成后,才会订阅下一个;


  • exhaustMap


const { of,interval} = Rx;const { exhaustMap ,take,map } = RxOperators;
const namesObservable = of('A', 'B');
const http =(name)=>{ return interval(1000) .pipe( take(3), map(()=>of(`${name} 1`,`${name} 2`)) )}
namesObservable.pipe( exhaustMap (name => http(name)))
复制代码



exhaustMap 映射成内部 observable,忽略其他值直到该 observable 完成;


<hr>


OK,以上便是本篇分享。


觉得不错点个赞吧👍👍👍,您的鼓励,我的动力,坚持输出质量好文~~ 欢迎评论讨论


我是掘金安东尼,输出暴露输入,技术洞见生活。再会吧~~ 👋👋👋

发布于: 2022 年 08 月 11 日阅读数: 3
用户头像

安东尼陪你度过漫长编程岁月~ 2022.07.14 加入

社会我瓜哥,人狠话不多😎 微信 anthony1453,加我交个朋友😎 正联合【机械工业出版社】出版《程序员成长手册》,敬请期待😎 真正的大师,永远怀着一颗学徒的心(易)😎

评论

发布
暂无评论
5 张弹珠图彻底弄清 RxJS 的拉平策略:mergeMap、switchMap、concatMap、exhaustMap_前端_掘金安东尼_InfoQ写作社区