5月27日 21:26

RxJS 中如何处理错误?有哪些错误处理操作符?

核心答案

RxJS 中 Observable 一旦出错,整个流就会终止。常用的错误处理操作符有五个:

  • catchError — 捕获错误,返回替代 Observable,流继续
  • retry(n) — 出错后重新订阅,最多重试 n 次
  • retryWhen — 自定义重试策略(延迟、指数退避等)
  • finalize — 流结束(无论成功还是出错)时执行清理
  • onErrorResumeNext — 出错后跳到下一个 Observable 继续

其中 catchError 是面试最常考的,关键在于理解它的位置决定行为:放在 mergeMap 内部只捕获单条流错误,放在外部则整个流被替换。

catchError 的位置陷阱

typescript
// 错误写法:外层 catchError,一条失败整条流终止 source$.pipe( mergeMap(id => fetchData(id)), catchError(() => of(fallback)) // 任一请求失败,后续全部跳过 ); // 正确写法:内层 catchError,单条失败不影响其他 source$.pipe( mergeMap(id => fetchData(id).pipe( catchError(() => of(fallback)) // 只替换这一条 )) );

面试中经常追问这个区别:内层捕获让每条数据流独立容错,外层捕获则是兜底策略。

retry 与 retryWhen 怎么选

retry(3) 简单粗暴,立刻重试三次。实际项目中更常见带延迟的重试:

typescript
source$.pipe( retryWhen(errors => errors.pipe( scan((count, err) => { if (count >= 3) throw err; return count + 1; }, 0), delayWhen(count => timer(Math.pow(2, count) * 1000)) ) ) );

指数退避重试是生产环境的标准做法,面试能答出这个基本过关。注意 RxJS 7 之后 retryWhen 已废弃,推荐用 retry({ delay: ... }) 替代。

finalize 不是 finally

finalize 无论成功、出错还是取消订阅都会执行,适合释放资源(关闭连接、清除定时器等)。它不接收参数,拿不到错误信息,别跟 Promise 的 finally 混淆。

追问方向

  • catchError 返回 throwError 会怎样?——错误继续向上传播
  • retry 的重试是重新订阅还是重新执行?——重新订阅整个上游
  • Observable 出错后订阅者还能收到值吗?——不能,流已终止
标签:Rxjs