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

本文通过弹珠图详细讲解RxJS中的map操作符家族,包括mergeMap、switchMap、concatMap和exhaustMap,阐述它们在处理observable时的不同拉平策略,帮助开发者理解并掌握这些操作符的使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

image.png

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

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{ .. } 

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

Untitled.png

并且 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,它们能够提供不同方向的拉平策略。

我们再借助 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))
) 

Untitled 1.png

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))
) 

Untitled 2.png

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))
) 

Untitled 3.png

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))
) 

Untitled 4.png

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


OK,以上便是本篇分享。

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值