文章目录
5.2 错误处理(Error Handling)概述
在 React + RxJS/Reactive Programming 生态中,数据流(Stream)可能会因 网络错误、API 异常、数据处理失败 等原因抛出错误。如果未正确处理,错误可能导致整个数据流终止,影响用户体验。
错误处理的核心目标:
- 防止错误导致整个应用崩溃(Graceful Degradation)。
- 提供合理的错误恢复机制(Retry/Fallback)。
- 记录错误信息(Logging & Monitoring)。
React 生态中常见的错误处理方式包括:
onErrorReturn
/onErrorResume
(错误回退)retry
/retryWhen
(错误重试)
接下来,我们将深入探讨这些方法的 原理、实现方式及适用场景。
5.2.1 onErrorReturn
/ onErrorResume
(错误回退)
1. onErrorReturn
:提供默认值
核心思想:当数据流发生错误时,返回一个 默认值 替代错误,使流继续执行。
适用场景:
- 适用于 可预测的错误(如 API 404 返回默认数据)。
- 不希望错误中断数据流,但可以接受降级数据。
示例(RxJS):
import { of, throwError } from 'rxjs';
import { onErrorReturn } from 'rxjs/operators';
const source$ = throwError(new Error('API Failed')); // 模拟错误
source$
.pipe(
onErrorReturn(() => ({ data: 'Default Value' })) // 错误时返回默认值
)
.subscribe({
next: (val) => console.log('Received:', val), // 输出: Received: { data: 'Default Value' }
error: (err) => console.error('Error:', err), // 不会触发
});
底层原理:
onErrorReturn
会在error
发生时拦截错误,并调用回调函数返回一个默认值。- 该操作符会 终止原 Observable,并返回一个新的 Observable,发射默认值后
complete
。
2. onErrorResume
:切换备用数据流
核心思想:当主数据流失败时,切换到备用数据流(Fallback Stream)。
适用场景:
- 主 API 失败时,回退到缓存/备用 API。
- 多数据源冗余设计(如 CDN 回源)。
示例(RxJS):
import { of, throwError } from 'rxjs';
import { onErrorResumeWith } from 'rxjs/operators';
const mainApi$ = throwError(new Error('Main API Down'));
const fallbackApi$ = of({ data: 'Fallback Data' });
mainApi$
.pipe(
onErrorResumeWith(fallbackApi$) // 主API失败时切换到备用流
)
.subscribe({
next: (val) => console.log('Received:', val), // 输出: Received: { data: 'Fallback Data' }
error: (err) => console.error('Error:', err), // 不会触发
});
底层原理:
onErrorResumeWith
会在错误发生时 取消订阅原 Observable,并立即订阅备用 Observable。- 如果备用流也失败,错误会继续传播(除非嵌套使用多个
onErrorResumeWith
)。
5.2.2 retry
/ retryWhen
(错误重试)
1. retry
:固定次数重试
核心思想:在发生错误时,自动重新订阅数据流(可设定最大重试次数)。
适用场景:
- 临时性错误(如网络抖动、API 限流)。
- 需要 简单重试逻辑 的场景。
示例(RxJS):
import { throwError, timer } from 'rxjs';
import { retry } from 'rxjs/operators';
let attempts = 0;
const flakyApi$ = new Observable((subscriber) => {
attempts++;
if (attempts < 3) {
subscriber.error(new Error('API Failed')); // 前两次模拟失败
} else {
subscriber.next('Success on attempt ' + attempts);
subscriber.complete();
}
});
flakyApi$
.pipe(
retry(2) // 最多重试2次(共3次尝试)
)
.subscribe({
next: (val) => console.log('Received:', val), // 输出: Received: Success on attempt 3
error: (err) => console.error('Error:', err), // 如果第3次仍失败,会触发
});
底层原理:
retry(n)
会在每次错误时重新订阅源 Observable,最多n
次。- 如果所有重试均失败,错误会传递给
Subscriber
。
2. retryWhen
:高级条件重试
核心思想:通过 自定义逻辑控制重试策略(如指数退避、依赖条件重试)。
适用场景:
- 复杂重试策略(如指数退避、Token 刷新后重试)。
- 需要 动态调整重试行为(如根据错误类型决定是否重试)。
示例(RxJS:指数退避):
import { throwError, timer } from 'rxjs';
import { retryWhen, mergeMap, delay } from 'rxjs/operators';
const flakyApi$ = throwError(new Error('API Failed'));
flakyApi$
.pipe(
retryWhen((errors) =>
errors.pipe(
mergeMap((err, attempt) => {
if (attempt >= 3) {
return throwError(err); // 超过3次后放弃
}
const delayMs = 1000 * 2 ** attempt; // 指数退避:1s, 2s, 4s...
console.log(`Retry in ${delayMs}ms`);
return timer(delayMs); // 延迟后重试
})
)
)
)
.subscribe({
next: (val) => console.log('Received:', val),
error: (err) => console.error('Final Error:', err), // 输出: Final Error: Error: API Failed
});
底层原理:
retryWhen
接收一个Observable<Error>
,允许自定义重试逻辑。- 每次错误时,可以:
- 返回
Observable
延迟重试(如timer
)。 - 返回
throwError
终止重试。
- 返回
- 适用于 Token 刷新后重试:
retryWhen((errors) => errors.pipe( mergeMap((err) => { if (isTokenExpired(err)) { return refreshToken().pipe( // 先刷新Token mergeMap(() => timer(1000)) // 再延迟重试 ); } return throwError(err); // 非Token错误直接抛出 }) ) )
5.3 错误处理策略对比
方法 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
onErrorReturn |
提供默认值(如空数据、占位图) | 简单易用,确保流不中断 | 无法恢复真实数据 |
onErrorResume |
切换到备用数据源(如缓存/冗余API) | 支持多数据源冗余 | 备用数据可能不一致 |
retry |
临时性错误(如网络抖动) | 自动重试,减少手动处理 | 可能加重服务器负载(无限重试时) |
retryWhen |
复杂重试逻辑(如指数退避、Token刷新) | 灵活控制重试策略 | 实现复杂度较高 |
5.4 总结
错误回退(Fallback)
onErrorReturn
:适合静态降级(如默认值)。onErrorResume
:适合动态降级(如备用API)。
错误重试(Retry)
retry(n)
:适合简单重试(固定次数)。retryWhen
:适合高级策略(指数退避、条件重试)。
最佳实践
- 关键数据:优先使用
retryWhen
+onErrorResume
保证可用性。 - 非关键数据:使用
onErrorReturn
快速降级。 - 监控所有错误:在
subscribe
的error
回调中记录日志。
通过合理组合这些策略,可以构建 健壮、可恢复的 React 数据流应用。