RxJS 系列 – Error Handling Operators
前言
前几篇介绍过了
这篇继续介绍 Error Handling Operators.
参考
RxJS 錯誤處理 Operators (1) - catchError / finalize / retry / retryWhen
Docs – Error Handling Operators
Observable and Subscriber Handler Error
Observable 的流程是 : Observable init > pipe > subscribe
不经过 pipe 的情况下, 处理 error 的方式如下:
const obs = new Observable(subscriber => { subscriber.error('error message'); // throw 'error message'; // throw 也是可以 }); obs.subscribe({ error: errorMessage => console.log(errorMessage), // error message });
简单明了.
catchError
catchError 是 pipe 阶段的 error handle. 它是 operator 来的.
catch error and change to succeeded
const obs = new Observable(subscriber => { subscriber.error('error message'); }); obs .pipe( catchError(error => { console.log(error); // error message return of('succeeded'); }) ) .subscribe({ next: value => console.log(value), // succeeded error: errorMessage => console.log(errorMessage), // won't call });
catchError 接收到 error 后, 可以有几个处理方式. 上面这个是返回一个 "成功" 的 Observable, 这样 error 就不会在传下去了.
catch error and re-throw
catch error 然后继续往下 throw error,
throwError 方法之前介绍过了, 它是 Creation Operators 的一员.
catchError(error => { return throwError(() => 're-throw error'); // throw 're-throw error'; // 用 throw 也可以 })
小结
总之, catchError 要返回一个 Observable, 可以是 succeeded 或者是 error
catchError(error => { if (Math.random() < 0.5) { return throwError(() => 're-throw error'); // throw 're-throw error'; // 用 throw 也可以 } else { return of('succeeded'); } })
catch error and retry
除了 succeeded 和 error, 还有一种处理方式是 retry. 所谓 retry 就是 unsubscribe 当前的 stream, 重新 subscribe Observable 得到新的 stream (旧的 Observable 会 displose, 新的会 init)
catchError((error, caught) => { return caught; })
返回 catchError 的第二个参数 caught 就表示要 retry. retry 可能会导致死循环的哦.
所以必须要有条件, 避开死循环, 比如:
catchError((error, caught) => { if (Math.random() < 0.5) { return caught; // retry } else { return of('succeeded'); } })
delay retry
catchError((error, caught) => { return timer(2000).pipe(switchMap(() => caught)); })
返回一个 delay 的 Observable 就可以延后 retry 了. 这里用了 switchMap 把 timer 的值换成了 caught observable. (这个 operator 我还没有介绍过的)
retry count
要计算 count 只能开一个外部的 variable 做记入. 或者封装一个自定义 operator. 但更简单的方法是直接用 RxJS 提供的 retry 和 retryWhen operator. 下面会介绍.
retry
用 catchError + caught 实现 retry 太费劲了, 所以 RxJS 封装了 retry operator
retry({ count: 3, delay: 2000, resetOnSuccess: false, })
count 声明可以 retry 多少次
delay 声明 retry 的间隔时间, 有时候 error 是因为 server 繁忙, 只要等 1,2 秒在 retry 发 ajax 就可以解决了.
此外 delay 还支持更复杂的 config. 通过判断 error 和 retryCount 来决定要 delay 多少秒.
我们甚至可以返回 click$ 让 user 点击触发 retry.
也可以直接返回 throwError 结束 retry (即便还没有 hit 到 max retry count), 灵活就对了
delay: (error, retryCount) => timer(2000 * retryCount)
resetOnSuccess 用来表达当 retry 成功以后是否要 reset retry count,默认值是 false,通常 reset 是正确的,所以一般我都是 set 成 true。
有一个场景需要 false,那就是 Angular HttpClient.request upload file 的时候。
httpClient.request upload 的 event dispatch 是这样:
event for upload percentage 10% > 40% > 80% > 100% > response error > retry 1 > 10% 注意这里是 success,不是 error > 40% > 80% > 100% > response error > return 1
为什么不是 retry 2?因为 10% 是 success,然后 resetOnSuccess: true,所以第二次 response error 时,retry count 已经 reset 回到 1 了。
这种情况 resetOnSuccess 就不太合适。
retryWhen
已经废弃了, 改用 retry + delay option 实现吧.
一句话总结
catchError : 在 pipe 中 catch error, 3中处理, 成功, 继续 error, retry
retry : 用 catchError 做 retry 太费劲就有了 retry operator
retryWhen : 废弃了, 改用 retry + delay option 实现.