如何实现带固定间隔重试、无refCount的RxJS shareReplay缓存HTTP请求
HTTP请求带错误重试的可控缓存实现
要实现符合要求的缓存逻辑,核心是区分成功/失败状态,控制多播实例的重置时机,避免无订阅时残留重试定时器,最终实现代码如下:
可复用操作符封装
import { share, tap, delay, retryWhen } from 'rxjs/operators'; import { Observable } from 'rxjs'; /** * HTTP缓存操作符,成功后永久缓存,错误时仅活跃订阅下间隔重试 * @param retryDelay 重试间隔时间,单位毫秒,默认10000 */ export function cacheWithRetry<T>(retryDelay: number = 10000) { // 标记请求是否成功,成功后不再重置多播实例 let isRequestSuccess = false; return (source: Observable<T>) => source.pipe( // 请求成功后标记状态 tap({ next: () => { isRequestSuccess = true; } }), // 出错后按指定间隔重试 retryWhen(errors => errors.pipe(delay(retryDelay))), share({ // 出错不重置多播实例,保证重试逻辑正常运行 resetOnError: false, // 完成不重置多播实例,保留成功后的缓存 resetOnComplete: false, // 仅当请求未成功、订阅数归零的时候重置多播实例,销毁残留定时器 resetOnRefCountZero: () => !isRequestSuccess }) ); }
使用方式
data = this.http.get(url).pipe( cacheWithRetry(10000) );
逻辑符合度说明
- 多播:
share操作符原生支持多播特性,同一时间存在多个订阅者时,仅会对源HTTP请求发起一次订阅 - 成功后永久缓存:请求成功后
isRequestSuccess标记为true,订阅数归零后不会重置多播实例,后续新订阅直接复用已缓存的成功结果,无需重新发起请求 - 错误时仅活跃订阅重试:请求未成功时如果所有订阅都被取消,
resetOnRefCountZero返回true会直接重置多播实例,重试定时器也会同步销毁,不会有后台残留任务;仅当有新的活跃订阅时才会重新发起请求,按间隔重试直到成功
测试适配说明
使用fakeAsync编写测试时,只要在测试用例结束前取消所有对data的订阅,重试定时器就会被自动销毁,不会出现1 timer(s) still in the queue的报错。
内容的提问来源于stack exchange,提问作者Adassko
相关产品推荐
相关产品推荐

