---
# System prepended metadata

title: RxJS-switchMap和concatMap用法
tags: ['#語法']

---

---
title: '[Layout] Two columns'
---

::::info
**介紹switchMap和concatMap的使用情境與差異**


:::spoiler 目錄 :
描述
>- [描述](#描述)

switchMap
>- [說明](#switchMap-說明)
>- [特性](#switchMap-特性)
>- [使用情境](#switchMap-使用情境)

concatMap
>- [說明](#concatMap-說明)
>- [特性](#concatMap-特性)
>- [使用情境](#concatMap-使用情境)

switchMap和concatMap差異
>- [差異](#差異)
>- [範例](#範例)

結論
>- [總結](#結論)



:::
::::

<style>
.two-column-layout {
  column-count: 2; /* Set column number */
  column-gap: 20px;
  max-width: 100%;
  overflow: hidden;
}

/* Media query for mobile devices */
@media (max-width: 768px) {
  .two-column-layout {
    column-count: 1; /* Switch to single column on small screens */
    column-gap: 0;   /* Optional: Set gap to 0 for single column */
  }
}

.markdown-body, .ui-infobar {
    max-width: unset !important;
}

.two-column-layout ul, 
.two-column-layout ol {
  margin: 0;
  padding-left: 20px;
}

.two-column-layout strong {
  font-weight: bold;
}

.two-column-layout em {
  font-style: italic;
}
    
.two-column-layout h1,
.two-column-layout h2,
.two-column-layout h3,
.two-column-layout h4,
.two-column-layout h5,
.two-column-layout h6 {
    margin-top: 0;    
}
</style>
# 描述
`switchMap`和`concatMap`都會處理來自源 Observable 的每一個元素並返回一個新的 Observable，簡單來說就是如果你要同個時間處理多個異步Observable可以使用。

- 如果你希望 同時處理多個異步操作，但只關心最新的結果，使用 `switchMap`。
- 如果你希望 依次處理多個異步操作，且確保每個操作完成後再處理下一個，使用 `concatMap`。

# switchMap

## `switchMap` 說明  

`switchMap` 是 RxJS 中的一個操作符，常用於處理異步事件流，特別是在需要 **取消舊請求並只保留最新請求** 的場景，例如 **即時搜尋 (autocomplete)**。

---

## `switchMap` 特性  
🟢 **當新的 observable 進來時，會取消前一個**（避免並行請求）。  
🟢 **只保留最新的 observable 結果**，確保最新資料可用。  
🟢 **適用於 快速變動的輸入**（如表單輸入、鍵盤事件）。  

---

## `switchMap` 使用情境  
### 🔎 即時搜尋 (autocomplete)  
使用者在輸入框輸入關鍵字時，每次輸入都會發送一個 API 請求，但我們只想要最後一次輸入的結果，而不是所有請求的結果。

### 🚀 避免多次請求覆蓋  
當使用者頻繁點擊按鈕時，`switchMap` 可以確保 **只保留最後一次點擊觸發的請求結果**，不會有多餘的回應。

&nbsp;

# concatMap
## `concatMap` 說明  

`concatMap` 是 RxJS 中的一個操作符，**用於確保每個 observable 順序執行**，適合需要 **前一個請求完成後才執行下一個請求** 的場景，例如 **API 需串聯執行的請求**。

---

## `concatMap` 特性  
🟢 **每個 observable 會按順序執行，上一個完成後才執行下一個**。  
🟢 **適合需要保持請求順序的場景**（如 API 依賴前一個結果時）。  
🟢 **確保不會同時發送多個請求，避免競態條件 (race conditions)**。  

---

## `concatMap` 使用情境  
### 🔗 依序執行 API 請求  
當一個 API 需要依賴前一個請求的結果時，`concatMap` 可確保請求按照順序執行，避免請求交錯導致錯誤。

### 🚛 批次處理請求  
當需要一次發送多個請求，且每個請求都必須**等前一個完成後再執行**（例如依序上傳檔案），`concatMap` 可以確保不會同時發送多個請求，減少伺服器負擔。

---

# concatMap和switchMap 差異
## 差異

| operator   | 特性                                                    | 適合場景                                      |
|------------|---------------------------------------------------------|-----------------------------------------------|
| `switchMap` | 前一個 observable 還沒結束時，如果來新的，**直接取消**前一個 | 避免多重觸發，取最新（像 Input欄位及時驗證API） |
| `concatMap` | 一個 observable **執行完才接下一個**，順序保證         | API 要確保串接、保證前後順序時           |

## 範例
描述:要一次打兩個API
- concatMap 版本
```
this.doPostData() // 1️⃣ 執行 doPostData()，取得第一個 API 資料
  .pipe(
    take(1), // 只取一次
    tap((res) => this.processPostData(res)), // 2️⃣ 這裡可以取得doPostData() 的資料，並處理
    concatMap(() => this.doPostTableData()), // 3️⃣ 等 doPostData 完全完成後，再接續 doPostTableData
    finalize(() => this.commonService.setLoadingFlag(false))
  )
  .subscribe((res) => { // 4️⃣ 這裡一樣拿到 doPostTableData() 的結果
    this.groupData = res.data.list;
    this.setPageInfo(res.data);
    if (Object.keys(this.groupData).length === 0) {
      alert('查無資料');
    }
  });
```
- concatMap 流程圖
```
開始
  ↓
執行 doPostData() (API 1)
  ↓
處理 API 1 資料 (tap -> processPostData)
  ↓
執行 doPostTableData() (API 2)(concatMap)
  ↓
關閉 Loading (finalize)
  ↓
處理 API 2 資料 (subscribe -> groupData, setPageInfo)
  ↓
資料為空？ → 是 → 顯示提示訊息 (alert)
              ↓ 否
結束
```

- switchMap 版本
```
this.doPostData() // 1️⃣ 執行 doPostData()，取得第一個 API 資料
  .pipe(
    take(1),  // 只取一次
    tap((res) => this.processPostData(res)), // 2️⃣ 這裡可以取得 doPostData() 的資料，並處理
    switchMap(() => this.doPostTableData()), // 3️⃣ 再接著執行第二個 API
    finalize(() => this.commonService.setLoadingFlag(false)) // 4️⃣ 兩個 API 都跑完或失敗時關閉 loading
  )
  .subscribe((res) => { // 5️⃣ 這裡的 res 是 **doPostTableData() 的資料**
    this.groupData = res.data.list;
    this.setPageInfo(res.data); // 設定分頁資訊
    if (Object.keys(this.groupData).length === 0) {
      alert('查無資料');
    }
  });
```
- switchMap流程圖
```
開始
  ↓
執行 doPostData() (API 1)
  ↓
處理 API 1 資料 (tap -> processPostData)
  ↓
執行 doPostTableData() (API 2)(switchMap)
  ↓
關閉 Loading (finalize)
  ↓
處理 API 2 資料 (subscribe -> groupData, setPageInfo)
  ↓
資料為空？ → 是 → 顯示提示訊息 (alert)
              ↓ 否
結束
```
- 流程差異總結

| 特性               | switchMap                             | concatMap                              |
|--------------------|--------------------------------------|----------------------------------------|
| 內部 Observable 行為 | 取消之前的 Observable，訂閱最新的     | 排隊執行，確保每個 Observable 順序執行  |
| 適用場景            | 即時性操作，例如搜尋建議             | 需要確保執行順序，例如多步驟 API 流程   |
| 資料處理順序        | 可能跳過中間的資料處理               | 確保每個資料按順序處理                |
| 優點                | 避免處理過時資料，提升即時性         | 確保執行順序，適合依賴前一步結果的流程 |
| 缺點                | 可能導致資料丟失                     | 可能增加延遲，因為需要等待前一步完成   |




---

# 結論

- 如果需要確保 API 執行的順序，應使用 `concatMap`。
- 如果只需要最新的結果，且不在意中間的資料被取消，應使用 `switchMap`。


