5月27日 21:47

RxJS 中 switchMap、mergeMap、concatMap 有什么区别?常用操作符有哪些?

答案:六类核心操作符

面试中最常考的 RxJS 操作符分六类:

创建类offromintervaltimer — 把同步值、数组、Promise、定时器转为 Observable。

转换类map 逐值转换;switchMap 切换到新 Observable 并取消前一个(搜索框场景首选);mergeMap 并行处理所有内部 Observable(不取消,不保证顺序);concatMap 顺序处理,上一个完成才订阅下一个。

过滤类filter 条件过滤;take(n) 只取前 n 个;takeUntil(notifier) notifier 发出时终止;debounceTime 停顿后才发出(防抖);throttleTime 间隔内只取第一个(节流)。

组合类merge 并行合并;concat 顺序连接;combineLatest 任一更新都组合最新值;zip 按索引一一配对。

错误处理类catchError 捕获并返回替代 Observable;retry(n) 失败后自动重试 n 次。

工具类tap 执行副作用不改值(调试利器);delay 延迟发射;distinctUntilChanged 连续重复值只发一次。

switchMap vs mergeMap vs concatMap:面试最高频对比

三者的核心区别在于对前一个内部 Observable 的处理策略

  • switchMap:新值到来 → 取消上一个 → 订阅新的。搜索框、路由切换等竞态场景必选,保证只拿到最新结果。
  • mergeMap:全部并行订阅,互不干扰。适合批量请求、日志上报等不关心顺序的场景。可传第二参数控制并发数。
  • concatMap:排队串行,上一个完成才订阅下一个。适合顺序敏感的写操作,如依次保存表单步骤。
javascript
// switchMap:搜索框 — 自动取消旧请求,避免竞态 fromEvent(input, 'input').pipe( debounceTime(300), switchMap(query => http.get('/search', { query })) ) // mergeMap:批量删除 — 限制3并发 ids.pipe(mergeMap(id => http.delete(`/item/${id}`), 3)) // concatMap:串行保存 — 保证顺序 actions.pipe(concatMap(action => http.post('/save', action)))

选错操作符的后果:搜索场景用 mergeMap 会显示旧请求结果覆盖新结果;表单保存用 mergeMap 可能后发先至导致数据错乱。

高频实战组合模式

javascript
// 搜索防抖 + 去重 + 空值过滤 + 自动取消旧请求 searchInput.pipe( debounceTime(300), distinctUntilChanged(), switchMap(query => query.length > 0 ? searchAPI(query) : EMPTY) ) // 表单自动保存(去重 + 防抖) form.valueChanges.pipe( debounceTime(1000), distinctUntilChanged(), switchMap(data => saveAPI(data)) )

追问方向

  • combineLatest vs zip 的区别?— combineLatest 任一更新即触发输出,zip 等所有 Observable 都发了新值才按索引配对
  • 如何避免内存泄漏?— takeUntil(destroy$\) 模式,组件销毁时 destroy$.next() 统一取消,优于手动 unsubscribe
  • shareReplay(1) 解决什么问题?— 多订阅者共享同一数据源并缓存最新值,避免重复 HTTP 请求
  • exhaustMap 是什么?— 忽略新值直到当前内部 Observable 完成,适合防止重复提交(登录按钮连击)
  • forkJoin vs combineLatest?— forkJoin 等所有完成后只取各自最后一个值,类似 Promise.all;combineLatest 每次变化都触发
标签:Rxjs