重学 Rxjs —— 错误处理操作符

646 阅读3分钟

「这是我参与2022首次更文挑战的第29天,活动详情查看:2022首次更文挑战

对于一个完整的程序,对可预见的错误添加异常处理逻辑是必不可少的。在 Rxjs 中针对这个常见的需求也提供了一系列错误处理操作符,本节就来看一下这部分内容。

catchError

catchError 就是普通的 catch 的语义(叫 catchError 是为了避免和关键字重复),它的效果就是捕获 Observable 中抛出的错误,举个例子:

of(0, 1, 2, 3).pipe(
	map(n => {
		if (n === 2) {
			throw new Error('error');
		}
		return n;
	}),
	catchError(err => of('catchError')),
).subscribe(x => console.log(x));
// 0
// 1
// catchError

可以看到当有错误抛出时,程序会流转到 catchError 中,catchError 可以用一个新的 Observable 作为返回值,此时订阅者可以收到新的 Observable 发出的数据。

使用弹珠图描述:

image.png

程序中的错误有很多种,在不同的情况下我们需要根据场景做对应处理,一个很常见的需求就是发生错误时执行重试逻辑。catchError 还有第二个参数,我们可以利用这个参数实现重试的效果:

of(0, 1, 2, 3).pipe(
	map(n => {
		if (n === 2) {
			throw new Error('error');
		}
		return n;
	}),
	catchError((err, caught) => caught),
).subscribe(x => console.log(x));
// 0
// 1
// 0
// 1
// ...

catchError 直接返回第二个参数,当发生错误时就会重复原始的 Observable。不过在实际开发中想要实现错误重试不需要这么麻烦,retry 操作符是用来专门处理这个场景的。

retry

retry 操作符可以在出现错误时进行重试,可以接收一个重试次数作为参数,默认为无限次数,借助 retry 我们可以很容易地实现前一个例子的效果:

of(0, 1, 2, 3).pipe(
	map(n => {
		if (n === 2) {
			throw new Error('error');
		}
		return n;
	}),
	retry(),
).subscribe(x => console.log(x));
// 0
// 1
// 0
// 1
// ...

可以使用弹珠图描述 retry 的过程(retry 用于存在错误时的重试,正常结束的流重试逻辑需要使用 repeat 操作符):

image.png

retryWhen

在实际开发中,有的时候需要的不一定是出现错误就重试,往往是需要伴随着一系列的条件,这里 Rxjs 提供了另一个和重试相关的操作符 retryWhen,在 retryWhen 中我们可以进一步处理重试相关逻辑。retryWhen 的用法不太容易描述,可以看一个例子来理解:

of(1, 2, 3, 4).pipe(
  map((val) => {
    if (val > 2) {
      throw val;
    }
    return val;
  }),
  retryWhen((errors) =>
    errors.pipe(
      delay(2000)
    )
  )
).subscribe(x => console.log(x));

可以看到 retryWhen 中可以拿到 errors 对象,我们在 errors 可以读取到发生错误时的原始 Observable 信息,之后根据具体情况进行处理,这里的处理是延迟 2 秒,这样的效果就是先打印 1、2,之后发生错误进入 retryWhen 逻辑,等待两秒 retryWhen 中的 Observable 触发时再执行重试,接着打印出 1、2,不断重复。

弹珠图描述:

image.png

在 Rxjs 中,和错误处理相关的操作符主要就是这么多,虽然异常处理不在主程序流程中,但是实际开发中还是必不可少的。