RxJS 高階映射操作符詳解:map、mergeMap 和 switchMap

1. map 操作符

map 是最基本的轉換操作符,用于對 Observable 發出的每個值進行一對一轉換。

基本特點:

  • 同步操作
  • 一對一轉換
  • 不改變 Observable 的發出時機

詳細示例:

import { of } from 'rxjs';
import { map } from 'rxjs/operators';// 示例1:簡單數值轉換
of(1, 2, 3).pipe(map(x => x * 2)
).subscribe(result => console.log(result));
// 輸出: 2, 4, 6// 示例2:對象屬性轉換
const users = of({ id: 1, name: 'Alice', age: 25 },{ id: 2, name: 'Bob', age: 30 }
);users.pipe(map(user => ({...user,name: user.name.toUpperCase(),isAdult: user.age >= 18}))
).subscribe(console.log);
/* 輸出:
{id: 1, name: "ALICE", age: 25, isAdult: true}
{id: 2, name: "BOB", age: 30, isAdult: true}
*/// 示例3:API響應處理
import { from } from 'rxjs';function fetchUser(userId) {return from(fetch(`https://jsonplaceholder.typicode.com/users/${userId}`).then(response => response.json()));
}fetchUser(1).pipe(map(user => user.name)
).subscribe(name => console.log('用戶名:', name));
// 輸出: 用戶名: Leanne Graham

2. mergeMap (flatMap) 操作符

mergeMap 用于將一個值映射成一個新的 Observable,并同時訂閱所有這些內部 Observable,合并它們的輸出。

基本特點:

  • 一對多轉換
  • 同時維護多個內部訂閱
  • 不取消之前的訂閱
  • 適合并行處理

詳細示例:

import { of, interval } from 'rxjs';
import { mergeMap, take } from 'rxjs/operators';// 示例1:將每個值轉換為新的Observable
of('a', 'b', 'c').pipe(mergeMap(letter => interval(1000).pipe(take(3),map(i => letter + i)))
).subscribe(console.log);
/* 輸出 (每秒一個):
a0
b0
c0
a1
b1
c1
a2
b2
c2
*/// 示例2:實際API請求
function fetchPosts(userId) {return from(fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`).then(response => response.json()));
}of(1, 2, 3).pipe(mergeMap(userId => fetchPosts(userId))
).subscribe(posts => {console.log(`用戶${posts[0]?.userId}的帖子數:`, posts.length);
});
/* 輸出:
用戶1的帖子數: 10
用戶2的帖子數: 10
用戶3的帖子數: 10
*/// 示例3:結合Promise使用
function uploadFile(file) {return new Promise(resolve => {setTimeout(() => {resolve(`文件${file.name}上傳成功`);}, 1000);});
}const files = of({ name: 'document.pdf' },{ name: 'image.jpg' }
);files.pipe(mergeMap(file => from(uploadFile(file)))
).subscribe(result => console.log(result));
/* 輸出 (大約同時):
文件document.pdf上傳成功
文件image.jpg上傳成功
*/

3. switchMap 操作符

switchMap 會在每次發出新值時取消之前的內部 Observable 訂閱,并訂閱新的 Observable。

基本特點:

  • 一對多轉換
  • 只保留最新的內部訂閱
  • 自動取消之前的訂閱
  • 適合搜索輸入等場景

詳細示例:

import { fromEvent, interval } from 'rxjs';
import { switchMap, take } from 'rxjs/operators';// 示例1:基礎用法
fromEvent(document.getElementById('searchInput'), 'input').pipe(switchMap(event => {const query = event.target.value;return from(fetch(`https://api.example.com/search?q=${query}`).then(res => res.json()));})
).subscribe(results => {console.log('搜索結果:', results);
});// 示例2:與interval結合
const source$ = of('start').pipe(switchMap(() => interval(1000).pipe(take(5),map(i => `${i}`)))
);source$.subscribe(console.log);
/* 輸出 (每秒一個):
值 0
值 1
值 2
值 3
值 4
*/// 示例3:實際應用 - 取消之前的請求
function searchBooks(query) {return from(fetch(`https://openlibrary.org/search.json?q=${query}`).then(res => res.json()));
}const searchInput = document.getElementById('bookSearch');
fromEvent(searchInput, 'input').pipe(map(event => event.target.value),filter(query => query.length > 2), // 至少3個字符才搜索debounceTime(300), // 防抖300msdistinctUntilChanged(), // 值變化才繼續switchMap(query => searchBooks(query))
).subscribe(results => {console.log('找到書籍:', results.docs);
});

4. tap 操作符

tap 用于在 Observable 流中執行副作用操作,不影響數據流。

基本特點:

  • 用于調試、日志記錄
  • 執行副作用但不修改數據
  • 不影響原始流

詳細示例:

import { of } from 'rxjs';
import { tap, map } from 'rxjs/operators';// 示例1:基本調試
of(1, 2, 3).pipe(tap(value => console.log('收到原始值:', value)),map(x => x * 2),tap(value => console.log('轉換后的值:', value))
).subscribe();
/* 輸出:
收到原始值: 1
轉換后的值: 2
收到原始值: 2
轉換后的值: 4
收到原始值: 3
轉換后的值: 6
*/// 示例2:實際應用 - 記錄API請求
function getUser(userId) {return from(fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)).pipe(tap(response => {console.log(`請求用戶${userId}的狀態:`, response.status);if (!response.ok) {throw new Error('請求失敗');}}),switchMap(response => from(response.json())));
}getUser(1).pipe(tap(user => {console.log('成功獲取用戶:', user.name);// 可以在這里更新UI狀態document.getElementById('loading').style.display = 'none';}),catchError(error => {console.error('獲取用戶失敗:', error);return of(null);})
).subscribe();// 示例3:狀態管理
let requestCount = 0;function fetchData() {return from(fetch('/api/data')).pipe(tap(() => {requestCount++;console.log(`當前請求數: ${requestCount}`);document.getElementById('spinner').style.display = 'block';}),switchMap(response => response.json()),tap(() => {document.getElementById('spinner').style.display = 'none';}));
}

綜合對比示例

import { of, fromEvent } from 'rxjs';
import { map, mergeMap, switchMap, tap, delay } from 'rxjs/operators';// 模擬API函數
function simulateApi(id, delayTime) {return of(`結果 ${id}`).pipe(delay(delayTime));
}// 對比mergeMap和switchMap
const button = document.getElementById('myButton');fromEvent(button, 'click').pipe(map((_, index) => index + 1), // 點擊次數tap(count => console.log(`點擊 #${count}`)),// 嘗試切換這兩個操作符查看區別// mergeMap(count => simulateApi(count, 1000))switchMap(count => simulateApi(count, 1000))
).subscribe(result => console.log('收到:', result));/* 
使用mergeMap時的可能輸出:
點擊 #1
點擊 #2
點擊 #3
收到: 結果 1
收到: 結果 2
收到: 結果 3使用switchMap時的可能輸出:
點擊 #1
點擊 #2 (在1秒內點擊)
點擊 #3 (在1秒內點擊)
收到: 結果 3 (只收到最后一個結果)
*/

實際應用場景總結

  1. map

    • 簡單數據轉換
    • 提取對象屬性
    • 格式化數據
  2. mergeMap

    • 并行處理多個請求
    • 文件上傳
    • 需要保留所有內部訂閱的場景
  3. switchMap

    • 搜索輸入自動完成
    • 取消之前的HTTP請求
    • 路由導航
  4. tap

    • 調試和日志記錄
    • 執行UI更新等副作用
    • 監控狀態變化

記住選擇操作符的關鍵:

  • 是否需要并行處理?→ mergeMap
  • 是否需要取消之前的請求?→ switchMap
  • 是否需要簡單轉換?→ map
  • 是否需要執行副作用?→ tap

在 RxJS 的 pipe 中,操作符的執行順序是從上到下、從左到右的線性執行流程。讓我們通過你提到的 maptapswitchMap 組合來分析它們的執行順序。

基本執行順序規則

  1. 數據流經 pipe 的順序:從第一個操作符開始,依次傳遞給后續操作符
  2. 同步操作符(如 maptap):立即執行
  3. 異步/高階操作符(如 switchMap):會創建新的 Observable
  4. 每個操作符都會接收上一個操作符的輸出

典型組合示例分析

import { fromEvent } from 'rxjs';
import { map, tap, switchMap } from 'rxjs/operators';fromEvent(document.getElementById('searchInput'), 'input').pipe(tap(event => console.log('1. 原始事件:', event)),        // 第1步map(event => event.target.value),                      // 第2步tap(value => console.log('2. 提取的值:', value)),      // 第3步switchMap(query => fetch(`/api/search?q=${query}`))    // 第4步
).subscribe(response => {console.log('5. 最終結果:', response);                 // 第5步
});

詳細執行流程

假設用戶在搜索輸入框中輸入 “rxjs”:

  1. tap(event => console.log('1. 原始事件:', event))

    • 最先執行
    • 打印原始的 input 事件對象
    • 不影響數據流,將原樣傳遞事件對象
  2. map(event => event.target.value)

    • 從事件對象中提取輸入框的值
    • event 轉換為字符串值(如 “rxjs”)
  3. tap(value => console.log('2. 提取的值:', value))

    • 打印提取后的值
    • 不影響數據流,原樣傳遞值
  4. switchMap(query => fetch(/api/search?q=${query}))

    • 高階操作,會做以下事情:
      a. 取消之前可能未完成的 fetch 請求(如果是連續輸入)
      b. 發起新的 fetch 請求
      c. 將 Promise 轉換為 Observable
    • 等待 API 響應
  5. subscribe 回調

    • 最后執行
    • 收到 fetch 的響應對象

可視化執行順序

輸入事件 → tap(記錄原始事件) → map(提取值) → tap(記錄值) → switchMap(發起請求) → 訂閱接收響應

關鍵注意事項

  1. switchMap 的特殊行為

    • 當新值到達時,會取消前一個內部 Observable
    • 這意味著如果用戶快速連續輸入,只有最后一次請求會被處理
  2. tap 的位置影響

    // 示例1:tap在switchMap之前
    .pipe(tap(x => console.log('before switchMap', x)),switchMap(x => fetchData(x))
    )// 示例2:tap在switchMap之后
    .pipe(switchMap(x => fetchData(x)),tap(x => console.log('after switchMap', x))
    )
    
    • 示例1的 tap 記錄的是 switchMap 前的值
    • 示例2的 tap 記錄的是 API 返回的數據
  3. 錯誤處理

    • 如果 switchMap 內部的 Observable 出錯,錯誤會跳過后續操作符直接到達 subscribe 的 error 回調

實際場景執行示例

假設我們有一個搜索框,用戶依次輸入:r → rx → rxj → rxjs

fromEvent(searchInput, 'input').pipe(debounceTime(300),map(event => event.target.value),tap(query => console.log(`正在處理查詢: ${query}`)),switchMap(query => from(fetch(`/api/search?q=${query}`).then(res => res.json()))
).subscribe({next: results => console.log('搜索結果:', results),error: err => console.error('搜索失敗:', err)
});

可能的執行過程:

  1. 用戶輸入 ‘r’ → 300ms 沒有新輸入 → 觸發搜索

    • tap: “正在處理查詢: r”
    • switchMap: 發起搜索 “r” 的請求
  2. 用戶在 300ms 內繼續輸入 ‘rx’ → 取消 ‘r’ 的請求

    • tap: “正在處理查詢: rx”
    • switchMap: 發起搜索 “rx” 的請求
  3. 用戶繼續快速輸入 ‘rxj’ → 取消 ‘rx’ 的請求

    • tap: “正在處理查詢: rxj”
    • switchMap: 發起搜索 “rxj” 的請求
  4. 用戶最后輸入 ‘rxjs’ 并停止 300ms → 取消 ‘rxj’ 的請求

    • tap: “正在處理查詢: rxjs”
    • switchMap: 發起搜索 “rxjs” 的請求
    • 最終只有 “rxjs” 的搜索結果會到達 subscribe

總結執行順序要點

  1. 線性流水線處理:數據像水流一樣依次通過每個操作符
  2. 同步操作符立即執行:map 和 tap 會立即對每個值進行處理
  3. switchMap 是異步邊界:它會開啟一個新的異步上下文
  4. 訂閱只看到最終結果:subscribe 收到的是經過整個管道處理后的結果
  5. 錯誤會跳過后續步驟:任何操作符出錯都會直接跳到錯誤處理

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80969.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80969.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80969.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于stm32的多旋翼無人機(Multi-rotor UAV based on stm32)

由于一直在調試本項目,好久沒有發文章,最近本項目的PID調試初見成效!開始正文前首先感謝各位粉絲的支持,以及對本項目技術上支持的老師以及師兄,謝謝你們! 對應源碼及文件:源碼及文件下載 基于…

量子傳感器:開啟微觀世界的精準探測

隨著量子技術的飛速發展,量子傳感器逐漸成為前沿科技領域的熱門研究方向。量子傳感器利用量子力學的特性,能夠實現對物理量的極高精度測量,其應用范圍涵蓋了基礎科學研究、醫學診斷、環境監測以及國防安全等多個領域。本文將深入探討量子傳感…

河道管網排口在線監測系統解決方案

一、方案概述 我國作為世界上河流數量最為豐富的國家之一,擁有眾多歷史悠久的壯闊江河流域。然而,伴隨經濟社會的迅猛發展,河湖管理與保護面臨諸多新挑戰,諸如河道干涸、湖泊萎縮、水環境惡化以及河湖功能退化等問題,對…

Foldseek快速蛋白質結構比對

1. 下載和安裝 Foldseek 如果只是單個蛋白質結構的序列比對,我們只需要用Foldseek 的網站服務 https://search.foldseek.com/search 上傳我們的蛋白質結構并選擇想要進行比對的數據庫即可,這里不做重點講解。做生物信息學研究,我們難免需要批…

宏山激光韓國釜山開放日圓滿舉行,服務本地化再提速

5月21日-22日,宏山激光在韓國釜山展廳舉辦了主題為“韓國本地服務領導者”的開放日活動,此次活動聚焦韓國市場,通過沉浸式參觀和深度交流,全面展示宏山激光本地化服務體系的建設成果,彰顯其服務本地、深耕市場的堅定決…

大模型「瘦身」指南:從LLaMA到MobileBERT的輕量化部署實戰

大模型「瘦身」指南:從LLaMA到MobileBERT的輕量化部署實戰 系統化學習人工智能網站(收藏):https://www.captainbed.cn/flu 文章目錄 大模型「瘦身」指南:從LLaMA到MobileBERT的輕量化部署實戰摘要引言一、輕量化技術…

JavaScript篇:函數作用域與作用域鏈探秘

大家好,我是江城開朗的豌豆,一名擁有6年以上前端開發經驗的工程師。我精通HTML、CSS、JavaScript等基礎前端技術,并深入掌握Vue、React、Uniapp、Flutter等主流框架,能夠高效解決各類前端開發問題。在我的技術棧中,除了…

Robust Kernel Estimation with Outliers Handling for Image Deblurring論文閱讀

Robust Kernel Estimation with Outliers Handling for Image Deblurring 1. 論文的研究目標與實際問題意義1.1 研究目標1.2 實際問題與產業意義2. 論文的創新方法、模型與優勢2.1 核心思路2.2 關鍵公式與技術細節2.2.1 非線性模糊模型與能量函數2.2.2 中間潛像更新與IRLS2.2.3…

nginx配置跨域請求,后臺不用配置啦,完美

允許全部把域名改* server { listen 22222; server_name localhost; location / { if ($request_method OPTIONS) { add_header Access-Control-Allow-Origin http://localhost:8080; add_header Access-Control-Allow-Headers *; add_header Access-Control-…

[特殊字符] 構建高內聚低耦合的接口架構:從數據校驗到后置通知的分層實踐

在現代企業系統開發中,接口結構設計的質量直接影響系統的穩定性、擴展性與可維護性。隨著業務復雜度上升,單一層次的接口實現往往難以應對功能膨脹、事務一致性、后置擴展等需求。因此,我們提出一種面向復雜業務場景的接口分層模型&#xff0…

MySQL 5.7 實戰:JSON 字段提取、Base64 解碼與引號問題全解析

一、背景與問題場景 在 MySQL 數據庫中,存儲 JSON 格式數據(如用戶行為日志、配置參數、擴展信息)的場景日益普遍。當需要從 JSON 字段中提取特定鍵值(如info)并進行 Base64 解碼時,常遇到以下問題&#x…

1.2.1+1.2.2計算機硬件的基本組成

知識總覽 早期馮諾依曼計算機:從人工-》自動 出現原因: 埃尼阿克計算機每執行一條指令都需要人工接線攬,雖然計算機處理的快,但是人工接線可能慢,效率低,于是出現馮諾依曼計算機,把要執行的指…

Spring AI 1.0 GA 正式發布

Spring AI 1.0 GA 正式發布 快速入門核心特性1. **增強型 LLM(大語言模型)**2. **MCP 協議支持**3. **RAG(檢索增強生成)**4. **評估與監控**5. **智能代理(Agents)** 下一步計劃 VMware Spring 團隊 Mark …

亞馬遜云科技推出Anthropic新一代模型

5月23日 亞馬遜云科技宣布在Amazon Bedrock中推出Anthropic的最新一代模型Claude Opus 4和Claude Sonnet 4。這兩款全新混合推理模型能夠根據需求在快速響應和深度思考模式間靈活切換,為編碼、高級推理和多步驟工作流領域帶來全新標準。它們不僅能在復雜的長時間推理…

無人機開啟未來配送新篇章

低空物流(無人機物流)是利用無人機等低空飛行器進行貨物運輸的物流方式,依托低空空域(通常在120-300米)實現快速、高效、靈活的配送服務。它是低空經濟的重要組成部分,廣泛應用于快遞配送、醫療物資運輸、農…

數據賦能(234)——數據管理——標準化原則

概述 標準化原則的重要性體現在確保數據的格式、結構和命名的一致性。這不僅可以提高數據的質量,還能促進數據的有效共享、交換和利用。以下是標準化原則的重要性的具體體現: 提高數據通用性:遵循數據標準和規范,確保不同系統、…

【Linux筆記】——線程池項目與線程安全單例模式

🔥個人主頁🔥:孤寂大仙V 🌈收錄專欄🌈:Linux 🌹往期回顧🌹: 【Linux筆記】——簡單實習一個日志項目 🔖流水不爭,爭的是滔滔不息 一、線程池設計二…

28-FreeRTOS內核控制-延時-臨界區

一、FreeRTOS的內核控制接口分析 1.1 函數taskYIELD 此函數用于進行任務切換,此函數本質上是一個宏。它允許當前任務主動放棄CPU使用權,將控制權轉移給調度器,以便調度器可以選擇另一個就緒任務運行。taskYIELD通常用于協作式多任務系統中&am…

NtfsLookupAttributeByName函數分析之和Scb->AttributeName的關系

第一部分: VOID FindFirstIndexEntry ( IN PIRP_CONTEXT IrpContext, IN PSCB Scb, IN PVOID Value, IN OUT PINDEX_CONTEXT IndexContext ) { 。。。。。。 // // Lookup the attribute record from the Scb. // if (!NtfsLookupAt…

關閉 Ubuntu 20.04 的 GNOME Shell和PulseAudio

一、GNOME Shell GNOME Shell 是 Ubuntu 20.04 默認的桌面環境管理器。關閉它會失去圖形界面(回到純終端模式),但可以節省內存和 CPU 資源。 方法 1:臨時關閉(當前會話生效) sudo systemctl stop gdm #…