Stories

Detail Return Return

5 張彈珠圖徹底弄清 RxJS 的拉平策略:mergeMap、switchMap、concatMap、exhaustMap - Stories Detail

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

我們在 [](https://rxviz.com/)https://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,它們能夠提供不同方向的拉平策略。

我們再借助 [](https://rxviz.com/)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))
)

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,以上便是本篇分享。

覺得不錯點個贊吧👍👍👍,您的鼓勵,我的動力,堅持輸出質量好文~~ 歡迎評論討論

我是掘金安東尼,輸出暴露輸入,技術洞見生活。再會吧~~ 👋👋👋

Add a new Comments

Some HTML is okay.