RxJs: 主要操作符大赏:switchMap,concatMap,takeUtil

251 阅读2分钟

switchMapmergeMapconcatMap 都是 RxJS 操作符,用于处理 Observable 的嵌套订阅,但它们的行为在处理 多个内部订阅 时有所不同:

1️⃣ switchMap

  • 特点切换到新的 Observable,取消之前的订阅。

  • 适用场景:适合搜索、输入框联想查询等,只关心最新的请求,避免并发。

  • 示例

    fromEvent(inputElement, 'input').pipe(
      switchMap(event => http.get(`/search?q=${event.target.value}`))
    ).subscribe(console.log);
    

    行为

    • 用户不断输入时,之前的请求会被取消,只保留最新的请求。

2️⃣ mergeMap

  • 特点同时处理多个内部 Observable,不会取消之前的订阅。

  • 适用场景:适合并发请求、批量处理任务等,不希望丢弃任何数据流。

  • 示例

    from([1, 2, 3]).pipe(
      mergeMap(id => http.get(`/user/${id}`))
    ).subscribe(console.log);
    

    行为

    • 1, 2, 3 的请求会并行执行,不会等待前一个完成。

3️⃣ concatMap

  • 特点按顺序执行,前一个完成后才执行下一个,类似 mergeMap + concat

  • 适用场景:适合需要按顺序执行的请求,如批量上传、事务处理等。

  • 示例

    from([1, 2, 3]).pipe(
      concatMap(id => http.get(`/user/${id}`))
    ).subscribe(console.log);
    

    行为

    • 1 的请求完成后,才会执行 2,然后再执行 3

🔥 总结

操作符是否并发是否取消前一个适用场景
switchMap❌(只保留最新)取消无用的请求(搜索、输入框)
mergeMap✅(并行)并发请求(批量 API 调用)
concatMap❌(顺序执行)按顺序执行(事务、上传)

如果任务可以并行执行,就用 mergeMap
如果需要保证顺序,就用 concatMap
如果只关心最新的请求,就用 switchMap

除了 switchMapmergeMapconcatMap,还有以下RxJS 操作符:


1️⃣ map(映射数据)

作用:对流中的每个值进行转换(同步转换)。
常见场景:修改 API 返回的数据格式。

of(1, 2, 3).pipe(
  map(x => x * 10)
).subscribe(console.log); // 输出 10, 20, 30

2️⃣ filter(过滤数据)

作用:筛选符合条件的值。
常见场景:表单验证、筛选列表数据。

of(1, 2, 3, 4, 5).pipe(
  filter(x => x % 2 === 0)
).subscribe(console.log); // 输出 2, 4

3️⃣ debounceTime(防抖)

作用延迟一定时间后再发出最新值,防止频繁触发。
常见场景搜索框输入,只在用户停止输入后触发请求。

fromEvent(inputElement, 'input').pipe(
  debounceTime(300),
  map(event => event.target.value)
).subscribe(console.log);

4️⃣ throttleTime(节流)

作用:在一定时间内只执行一次,忽略期间的其他事件。
常见场景按钮防抖(防止用户短时间内多次点击)。

fromEvent(button, 'click').pipe(
  throttleTime(1000)
).subscribe(() => console.log('Clicked!'));

5️⃣ distinctUntilChanged(去重)

作用:当值变化时才触发,避免相同值重复触发。
常见场景搜索优化,防止相同输入重复触发请求。

fromEvent(inputElement, 'input').pipe(
  map(event => event.target.value),
  distinctUntilChanged()
).subscribe(console.log);

6️⃣ take & takeUntil(控制流的结束)

  • take(n): 只取前 n 个值后完成。
  • takeUntil(stop$): 当 stop$ 触发时结束流。
    常见场景只监听前 n 次事件手动控制订阅何时停止
interval(1000).pipe(
  take(3) // 只取 3 次后结束
).subscribe(console.log); // 输出 0, 1, 2
const stop$ = fromEvent(button, 'click');
interval(1000).pipe(
  takeUntil(stop$) // 当按钮点击时结束
).subscribe(console.log);

7️⃣ combineLatest(合并多个流)

作用:当任意一个流发生变化时,返回所有流的最新值。
常见场景动态表单的多个输入框,需要计算结果。

combineLatest([
  fromEvent(input1, 'input').pipe(map(e => e.target.value)),
  fromEvent(input2, 'input').pipe(map(e => e.target.value))
]).subscribe(([val1, val2]) => console.log(val1, val2));

8️⃣ forkJoin(等待所有请求完成)

作用所有流都完成后才发出最终值(适用于并行请求)。
常见场景:加载多个 API,等数据都返回后再渲染页面。

forkJoin({
  user: http.get('/user'),
  posts: http.get('/posts')
}).subscribe(result => console.log(result));

📌 区别combineLatest任意一个流变动都会触发,而 forkJoin 只在所有流完成时触发一次


💡 总结

操作符作用常见场景
map同步转换数据API 数据转换
filter过滤数据列表筛选
debounceTime防抖输入框搜索
throttleTime节流按钮防抖
distinctUntilChanged去重避免重复 API 请求
take / takeUntil结束流监听有限次数 / 手动停止订阅
combineLatest合并多个流(任何变化都会触发)计算多个输入框的值
forkJoin等待所有请求完成后再触发多个 API 并行加载