RXJS 常用操作符总结

305 阅读4分钟

RxJS 是一个用于处理异步和事件流的库,提供了许多强大的操作符来处理 Observables,使得用户对于数据流的处理更加的高效。我们可以通过操作符轻松组合和变换多个数据流,同时它支持多种数据源的处理,包括事件、HTTP 请求、WebSocket 等。操作符在对于RXJS的应用中是不可或缺的一环。

前文:浅析RXJS


基本操作符
  1. 创建操作符
  • of():创建一个 Observable,发出指定的值。

  • from():从一个数组、Promise 或其他可迭代对象创建 Observable。

import { from } from 'rxjs';
import { of, from, interval } from 'rxjs';

// 使用 of 创建一个 Observable
// of 用于从一组值创建一个 Observable。它可以接受任意数量的参数,并将这些参数作为单独的值发出。
const observableOf = of(1, 2, 3);

// 使用 from 创建一个 Observable
// from 用于将其他类型的对象(如数组、Promise、Iterable、Observable 等)转换为 Observable。
// 使用 from 来处理异步数据源或将集合转换为 Observable,适合于需要逐个发出值的场景。
const observableFrom = from([1, 2, 3]);
  1. 变换操作符
  • map():对 Observable 发出的每个值应用一个函数,并返回一个新的 Observable。

  • filter():过滤 Observable 发出的值,仅保留满足条件的值。

  • mergeMap()(也称为flatMap()):将每个发出的值映射为一个 Observable,并将这些 Observables 合并为一个

import { map } from 'rxjs/operators';
const doubled$ = source$.pipe(map(x => x * 2));

import { filter } from 'rxjs/operators';
const even$ = source$.pipe(filter(x => x % 2 === 0));

import { mergeMap } from 'rxjs/operators';
const source$ = of(1, 2, 3);
const combined$ = source$.pipe(mergeMap(x => of(x, x + 1)));


// 订阅 combined$,以查看输出  
combined$.subscribe(value => console.log(value));
// 当 x 为 1 时,of(1, 2) 会被创建;
// 当 x 为 2 时,of(2, 3) 会被创建;
// 当 x 为 3 时,of(3, 4) 会被创建。
// mergeMap() 会将这些新的 Observables 合并成一个单一的 Observable。
// 最终的输出顺序依次是 1, 2, 2, 3, 3, 4。
  1. 组合操作符
  • combineLatest():将多个 Observables 的最新值组合成一个数组。

  • forkJoin():等待所有 Observables 完成,并发出它们的最后一个值。

import { combineLatest } from 'rxjs';
// 当 obs1$ 或 obs2$ 中的任意一个发出新值时,combined$ 会发出一个包含这两个最新值的数组。
const combined$ = combineLatest([obs1$, obs2$]);


import { forkJoin } from 'rxjs';
// 如果有任何一个 Observable 没有完成,forkJoin() 将不会发出任何值。
const finalValues$ = forkJoin([obs1$, obs2$]);
  1. 处理异步操作
  • debounceTime():在发出值前,等待指定的时间,避免快速连续的触发。

  • switchMap():用于处理高频率的事件流,只保留最后一个发出的 Observable。

import { debounceTime } from 'rxjs/operators';
// 可以实现防抖
const debounced$ = source$.pipe(debounceTime(300));


import { switchMap } from 'rxjs/operators';
// 使用 throttleTime 实现节流
// 将每个发出的值映射为一个新的 Observable,并且只保留最后一个发出的 Observable 的结果。
// 它会在新的值发出时取消之前的 Observable,适用于处理高频率的事件流。
const throttled$ = source$.pipe(
  throttleTime(300), // 每 300 毫秒只允许一次事件触发
  switchMap(value => fetchData(value)) // 处理高频率的事件流
);
  1. 错误处理操作符
  • catchError():捕获 Observable 中的错误,并返回一个新的 Observable。

import { catchError } from 'rxjs/operators';
const safe$ = source$.pipe(catchError(err => of('Error occurred')));
  1. 结束操作符
  • take():只发出前 n 个值,然后完成。

  • takeUntil():直到另一个 Observable 发出值时完成。

import { take } from 'rxjs/operators';
const firstThree$ = source$.pipe(take(3));


import { takeUntil } from 'rxjs/operators';
const until$ = source$.pipe(takeUntil(stop$));
  1. pipe (...)

用于将多个操作符组合在一起,以便对 Observable 的数据流进行灵活地处理。

操作符的顺序非常重要,后面的操作符会基于前面的输出进行处理。 pipe() 方法返回一个新的 Observable,原始 Observable 不会被修改。

import {of} form 'rxjs';
import {map,filter} from 'rxjs/operators';


const source$=of(1,2,3,4,5);

const result$=source$.pipe(
    filter(value=>value%2===0),//过滤出偶数  
    map(value=>value*10)  //将每个偶数乘以10
);

// 订阅
result$.subscribe(result=>{
    console.log(result);
})

高级操作符
  1. 更多高级操作符,如 concatMap、exhaustMap、distinctUntilChanged 等。
  • concatMap():适用于需要按顺序处理每个值的场景。

    import { from } from 'rxjs';
    import { concatMap, delay } from 'rxjs/operators';
    
    const source$ = from([1, 2, 3]);
    
    // concatMap() 将每个发出的值映射为一个新的 Observable,并按顺序处理这些 Observables。
    // 它会等待前一个 Observable 完成后,再处理下一个。
    const result$ = source$.pipe(
      concatMap(value => of(`Processed ${value}`).pipe(delay(1000))) // 每个值延迟 1 秒处理
    );
    
    result$.subscribe(result => console.log(result));
    
  • exhaustMap():exhaustMap()会将每个发出的值映射为一个新的 Observable,但如果前一个 Observable 还在处理,则会忽略后续的值,直到前一个完成。

  • distinctUntilChanged():适用于去重场景,只发出与前一个值不同的值。

  1. 组合多个 Observables:使用 zip、forkJoin、concat 等方法组合多个 Observables。 这些组合操作符提供了不同的方式来处理多个 Observables:
  • zip():适用于按顺序组合多个 Observables 的值,只有当所有输入 Observables 都发出新值时,才会发出组合的值。
  • forkJoin():适用于等待所有 Observables 完成后一次性获取它们的最后值。
  • concat():适用于按顺序处理多个 Observables,每个只有在前一个完成后才能开始。

通过灵活使用这些组合操作符,可以有效地管理复杂的异步数据流。

  1. 错误处理:使用 catchError、retry、retryWhen 等操作符处理错误。
  • catchError():用于捕获 Observable 中的错误并返回一个新的 Observable,适用于处理错误并提供备选值的场景。

    import { of, throwError } from 'rxjs';
    import { catchError } from 'rxjs/operators';
    
    const source$ = throwError('Something went wrong!').pipe(
      catchError(err => {
        console.error('Caught error:', err);
        return of('Error handled, returning fallback value.');
      })
    );
    
    source$.subscribe(
      value => console.log('Value:', value),
      error => console.log('Error:', error)
    );
    
  • retry():用于在发生错误时重新订阅 Observable,可以指定重试的次数。

    import { of, throwError } from 'rxjs';
    import { retry, map } from 'rxjs/operators';
    
    let attempt = 0;
    
    const source$ = of('Hello').pipe(
      map(value => {
        if (attempt < 2) {
          attempt++;
          throw new Error('Simulated error');
        }
        return value;
      }),
    // 用于在 Observable 发生错误时重新订阅它。这里的参数 2 表示最多重试 2 次。
      retry(2) // 尝试重试 2 次
    );
    
    source$.subscribe(
      value => console.log('Value:', value),
      error => console.log('Error:', error.message)
    );
    
  • retryWhen():提供更灵活的重试机制,当 Observable 发生错误时,可以基于这个条件来决定是否重试。你可以通过返回一个新的 Observable 来控制重试的逻辑。

    返回一个 timer Observable 是一种常见的做法,通常用于在重试之间引入延迟。

    可以返回其他类型的 Observable,这样可以实现更灵活的重试逻辑。

    例如:

    • of():立即发出一个值,然后重试。

    • interval():发出值的间隔 Observable,用于定期重试

    • throw error:在重试次数达到上限时,抛出错误是一个常见的做法。这会导致整个 Observable 进入错误状态,并且会被后续的 catchError() 捕获。

    import { of, throwError, timer } from 'rxjs';
    import { retryWhen, mergeMap, catchError } from 'rxjs/operators';
    
    let attempt = 0;
    
    const source$ = of('Hello').pipe(
    // 用于将每个发出的值映射为一个新的 Observable。
      mergeMap(value => {
        if (attempt < 2) {
          attempt++;
          return throwError('Simulated error');
        }
        return of(value);
      }),
    // 当 Observable 发生错误时,使用这个操作符定义重试逻辑。
    // 通常返回的是一个控制重试逻辑的 Observable
      retryWhen(errors =>
        // 发出错误的 Observable。
        errors.pipe(
        // 使用 mergeMap,处理每个错误和重试的逻辑。
          mergeMap((error, index) => {
            console.log(`Attempt ${index + 1}: ${error}`);
            // 如果重试次数小于 2,则使用 timer(1000) 创建一个 Observable,在 1 秒后发出值,表示等待 1 秒后重试。
            if (index < 2) {
            // 返回一个 timer Observable,用于在重试之间引入延迟。
              return timer(1000); // 等待 1 秒后重试
            }
            throw error; // 超过重试次数,抛出错误
          })
        )
      ),
      catchError(err => of(`Error handled: ${err}`))
    );
    
    source$.subscribe(value => {
      console.log('Value:', value);
    });