﻿# 釐清幾個超容易混淆又很常用的 RxJS 運算子 (Operators)

撰寫 [Angular](https://angular.io/) 的開發者，目前應該是最自然接觸 [RxJS](https://rxjs.dev/) 的了。每當有機會要用上的時候，就會在這一百多個 RxJS 運算子之間徘迴打轉，因為實在太多相似的 API 可以用，以至於經常鬼打牆寫不出來。今天，我就整理幾個不但常用，命名還很像，更容易用錯的 RxJS 運算子。

### [](#記憶法分享)記憶法分享

在這麼多種不同的 RxJS 運算子之間，透過死背的方式，是一定行不通的。我個人的理解方式，首先是理解「英文」所代表的意思，然後再進行「聯想」，最後再去推算出每個運算子所代表的意思。

**關鍵字說明**

| 關鍵字 | 字義說明 |
| --- | --- |
| scan | 顧名思義【掃描】就是將傳入 RxJS 運算子的事件資料，一筆一筆處理過一遍，每次處理的過程都會累加，並且處理一筆就 emit 一筆新的事件資料出來。 |
| map | 顧名思義【對應】就是將一筆傳入的資料，對應到另一種格式的資料，通常用來轉換資料之用。 |
| concat | 顧名思義【串接】就是將一筆一筆的資料串接在一起，通常是把多個 Observable 物件串接成一個新的 Observable 物件。 |
| switch | 顧名思義【交換】就是將一筆資料 "交換成" 另一種資料，當連續交換的事件發生時，還沒交換的資料就會被放棄。 |
| merge | 顧名思義【合併】就是將多筆資料合併成一筆，通常是將多個 Observable 物件合併成一個 Observable 物件。 |
| flat | 其用途跟 merge 完全一樣，在語意上，通常代表把多個 Observable 物件「壓平」成一個 Observable 物件。 |
| exhaust | 顧名思義【耗盡】就是要把原本 Observable 物件送出的資料都跑完，才能繼續跑下一個。 |
| zip | 這個單字有【拉鍊】的意思，你要發揮一點想像力，假設衣服的兩端，拉鍊拉起來之後，每一節都會平整的被湊在一起。 |
| combine | 顧名思義【組合】就是將多筆資料組合在一起。 |
| forkJoin | 這裡的 fork 是【走進岔路】的意思，Join 則是【從不同的岔路合併回來】。通常意思代表多個 Observable 物件同時執行，但全部執行完之後，才會 emit 所有資料，等大家從岔路走回來的感覺。 |

對上述關鍵字有點理解後，我們再來看另外幾個關鍵字：

| 關鍵字 | 字義說明 |
| --- | --- |
| xxxAll | 顧名思義【全部】就是將所有傳入的 Observabe 物件，全部一起處理。 |
| xxxLatest | 顧名思義【最新的】就是取得最新資料的意思。 |
| xxxTo | 這裡的 To 有【表示結果】的意思，也就是直接把特定結果 emit 出去。 |
| xxxMap | 這裡的 Map 有【對應】的意思，但是在 RxJS 的領域中，通常代表著在 Operator 中會對應到另一個不同的 Observable 物件。 |
| xxxMapTo | 這裡就是 Map + To 的意思，但是在 RxJS 的領域中，通常代表著在 Operator 中會對應到另一個自行指定的 Observable 物件。 |
| xxxScan | 顧名思義【掃描】就是將傳入 RxJS 運算子的事件資料，一筆一筆處理過一遍，每次處理的過程都會累加，並且處理一筆就 emit 一筆新的事件資料出來。 |

了解這些關鍵字的意思後，接下來就是苦工了。那就是文件認真看，範例程式看懂，看不懂就看原始碼，原始碼看不懂就看單元測試程式，再看不懂就是到 [FB 社團](https://www.facebook.com/groups/augularjs.tw/)或討論區發問！

### [](#常見-rxjs-運算子-operators-簡表-新版文件)常見 RxJS 運算子 (Operators) 簡表 (新版文件)

| 關鍵字 | xxxAll | xxxLatest | xxxTo | xxxMap | xxxMapTo | xxxScan |
| --- | --- | --- | --- | --- | --- | --- |
| [scan](https://rxjs-dev.firebaseapp.com/api/operators/scan) | \*  | \*  | \*  | \*  | \*  | \*  |
| [map](https://rxjs-dev.firebaseapp.com/api/operators/map) | \*  | \*  | [mapTo](https://rxjs-dev.firebaseapp.com/api/operators/mapTo) | \*  | \*  | \*  |
| [concat](https://rxjs-dev.firebaseapp.com/api/operators/concat) | [concatAll](https://rxjs-dev.firebaseapp.com/api/operators/concatAll) | \*  | \*  | [concatMap](https://rxjs-dev.firebaseapp.com/api/operators/concatMap) | [concatMapTo](https://rxjs-dev.firebaseapp.com/api/operators/concatMapTo) | \*  |
| switch | [switchAll](https://rxjs-dev.firebaseapp.com/api/operators/switchAll) | \*  | \*  | [switchMap](https://rxjs-dev.firebaseapp.com/api/operators/switchMap) | [switchMapTo](https://rxjs-dev.firebaseapp.com/api/operators/switchMapTo) | \*  |
| [merge](https://rxjs-dev.firebaseapp.com/api/operators/merge) | [mergeAll](https://rxjs-dev.firebaseapp.com/api/operators/mergeAll) | \*  | \*  | [mergeMap](https://rxjs-dev.firebaseapp.com/api/operators/mergeMap) | [mergeMapTo](https://rxjs-dev.firebaseapp.com/api/operators/mergeMapTo) | [mergeScan](https://rxjs-dev.firebaseapp.com/api/operators/mergeScan) |
| flat | \*  | \*  | \*  | [flatMap](https://rxjs-dev.firebaseapp.com/api/operators/flatMap) | \*  | \*  |
| [exhaust](https://rxjs-dev.firebaseapp.com/api/operators/exhaust) | \*  | \*  | \*  | [exhaustMap](https://rxjs-dev.firebaseapp.com/api/operators/exhaustMap) | \*  | \*  |
| [zip](https://rxjs-dev.firebaseapp.com/api/index/function/zip) | [zipAll](https://rxjs-dev.firebaseapp.com/api/operators/zipAll) | \*  | \*  | \*  | \*  | \*  |
| combine | [combineAll](https://rxjs-dev.firebaseapp.com/api/operators/combineAll) | [combineLatest](https://rxjs-dev.firebaseapp.com/api/index/function/combineLatest) | \*  | \*  | \*  | \*  |
| [forkJoin](https://rxjs-dev.firebaseapp.com/api/index/function/forkJoin) | \*  | \*  | \*  | \*  | \*  | \*  |

### [](#常見-rxjs-運算子-operators-簡表-舊版文件)常見 RxJS 運算子 (Operators) 簡表 (舊版文件)

| 關鍵字 | xxxAll | xxxLatest | xxxTo | xxxMap | xxxMapTo | xxxScan |
| --- | --- | --- | --- | --- | --- | --- |
| [scan](https://rxjs.dev/api/operators/scan) | \*  | \*  | \*  | \*  | \*  | \*  |
| [map](https://rxjs.dev/api/operators/map) | \*  | \*  | [mapTo](https://rxjs.dev/api/operators/mapTo) | \*  | \*  | \*  |
| [concat](https://rxjs.dev/api/operators/concat) | [concatAll](https://rxjs.dev/api/operators/concatAll) | \*  | \*  | [concatMap](https://rxjs.dev/api/operators/concatMap) | [concatMapTo](https://rxjs.dev/api/operators/concatMapTo) | \*  |
| switch | [switchAll](https://rxjs.dev/api/operators/switchAll) | \*  | \*  | [switchMap](https://rxjs.dev/api/operators/switchMap) | [switchMapTo](https://rxjs.dev/api/operators/switchMapTo) | \*  |
| [merge](https://rxjs.dev/api/operators/merge) | [mergeAll](https://rxjs.dev/api/operators/mergeAll) | \*  | \*  | [mergeMap](https://rxjs.dev/api/operators/mergeMap) | [mergeMapTo](https://rxjs.dev/api/operators/mergeMapTo) | [mergeScan](https://rxjs.dev/api/operators/mergeScan) |
| flat | \*  | \*  | \*  | flatMap | \*  | \*  |
| [exhaust](https://rxjs.dev/api/operators/exhaust) | \*  | \*  | \*  | [exhaustMap](https://rxjs.dev/api/operators/exhaustMap) | \*  | \*  |
| [zip](https://rxjs.dev/api/index/function/zip) | [zipAll](https://rxjs.dev/api/operators/zipAll) | \*  | \*  | \*  | \*  | \*  |
| combine | [combineAll](https://rxjs.dev/api/operators/combineAll) | [combineLatest](https://rxjs.dev/api/operators/combineLatest) | \*  | \*  | \*  | \*  |
| [forkJoin](https://rxjs.dev/api/index/function/forkJoin) | \*  | \*  | \*  | \*  | \*  | \*  |

### [](#常見-rxjs-運算子-operators-簡表-原始碼)常見 RxJS 運算子 (Operators) 簡表 (原始碼)

| 關鍵字 | xxxAll | xxxLatest | xxxTo | xxxMap | xxxMapTo | xxxScan |
| --- | --- | --- | --- | --- | --- | --- |
| [scan](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/scan.ts) | \*  | \*  | \*  | \*  | \*  | \*  |
| [map](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/map.ts) | \*  | \*  | [mapTo](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/mapTo.ts) | \*  | \*  | \*  |
| [concat](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/concat.ts) | [concatAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/concatAll.ts) | \*  | \*  | [concatMap](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/concatMap.ts) | [concatMapTo](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/concatMapTo.ts) | \*  |
| switch | [switchAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/switchAll.ts) | \*  | \*  | [switchMap](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/switchMap.ts) | [switchMapTo](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/switchMapTo.ts) | \*  |
| [merge](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/merge.ts) | [mergeAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/mergeAll.ts) | \*  | \*  | [mergeMap](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/mergeMap.ts) | [mergeMapTo](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/mergeMapTo.ts) | [mergeScan](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/mergeScan.ts) |
| flat | \*  | \*  | \*  | flatMap | \*  | \*  |
| [exhaust](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/exhaust.ts) | \*  | \*  | \*  | [exhaustMap](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/exhaustMap.ts) | \*  | \*  |
| [zip](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/zip.ts) | [zipAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/zipAll.ts) | \*  | \*  | \*  | \*  | \*  |
| combine | [combineAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/combineAll.ts) | [combineLatest](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/operators/combineLatest.ts) | \*  | \*  | \*  | \*  |
| [forkJoin](https://github.com/ReactiveX/rxjs/blob/6.6.7/src/internal/observable/forkJoin.ts) | \*  | \*  | \*  | \*  | \*  | \*  |

### [](#常見-rxjs-運算子-operators-簡表-單元測試)常見 RxJS 運算子 (Operators) 簡表 (單元測試)

| 關鍵字 | xxxAll | xxxLatest | xxxTo | xxxMap | xxxMapTo | xxxScan |
| --- | --- | --- | --- | --- | --- | --- |
| [scan](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/scan-spec.ts) | \*  | \*  | \*  | \*  | \*  | \*  |
| [map](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/map-spec.ts) | \*  | \*  | [mapTo](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/mapTo-spec.ts) | \*  | \*  | \*  |
| [concat](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/concat-spec.ts) | [concatAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/concatAll-spec.ts) | \*  | \*  | [concatMap](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/concatMap-spec.ts) | [concatMapTo](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/concatMapTo-spec.ts) | \*  |
| switch | [switchAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/switch-spec.ts) | \*  | \*  | [switchMap](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/switchMap-spec.ts) | [switchMapTo](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/switchMapTo-spec.ts) | \*  |
| [merge](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/merge-spec.ts) | [mergeAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/mergeAll-spec.ts) | \*  | \*  | [mergeMap](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/mergeMap-spec.ts) | [mergeMapTo](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/mergeMapTo-spec.ts) | [mergeScan](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/mergeScan-spec.ts) |
| flat | \*  | \*  | \*  | flatMap | \*  | \*  |
| [exhaust](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/exhaust-spec.ts) | \*  | \*  | \*  | [exhaustMap](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/exhaustMap-spec.ts) | \*  | \*  |
| [zip](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/zip-spec.ts) | [zipAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/zipAll-spec.ts) | \*  | \*  | \*  | \*  | \*  |
| combine | [combineAll](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/combineAll-spec.ts) | [combineLatest](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/combineLatest-spec.ts) | \*  | \*  | \*  | \*  |
| [forkJoin](https://github.com/ReactiveX/rxjs/blob/6.6.7/spec/operators/forkJoin-spec.ts) | \*  | \*  | \*  | \*  | \*  | \*  |

有了上面四張大表，以後要是真的忘記 RxJS 運算子的用法，就可以很快的查到相關文件，或是單元測試文件了！

**相關連結**

-   [RxJS 6 API List](https://rxjs-dev.firebaseapp.com/api)
-   [RxJS 5 Manual](https://rxjs.dev/guide/overview)
-   [Learn RxJS](https://www.learnrxjs.io/) - Clear examples, explanations, and resources for RxJS.
-   [RxJS 6 原始碼](https://github.com/ReactiveX/rxjs/tree/6.6.7/src/internal/operators)
-   [RxJS 6 單元測試程式碼](https://github.com/ReactiveX/rxjs/tree/6.6.7/spec/operators)
-   [RxJS 與時間相關的 Operators 比較](https://gist.github.com/doggy8088/e324e393e4f29ce3275a04b616fdcafc)
