--- 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`。