RxJs:外部Observable的retry不生效,如何重试获取更新后的参数?
问题分析
你的外部retry不生效,核心原因有两个:
catchError拦截错误后返回throwError,虽然错误会传递到下游,但request$(基于Component Store动作的流)是冷Observable,重新订阅不会产生新的发射,导致withLatestFrom无法重新执行去获取最新的store状态。- 内部
retry仅重试API调用,但bigPayload是在switchMap执行时捕获的旧值——即使你在错误回调里清空了store,重试时依然会用原来的大payload,达不到预期效果。
解决方案
方案一:用retryWhen结合状态变化触发重试
retryWhen可以让你控制重试的时机,比如等待store的bigPayload被清空后,再重新执行整个流逻辑,确保获取最新的状态:
request$.pipe( withLatestFrom( this._store.select(state => state.bigPayload), this._store.select(state => state.somethingElse) ), switchMap(([{ request }, bigPayload, somethingElse]) => { return this._service.callApi(bigPayload).pipe( tap(resp => { // 成功逻辑处理 }), catchError(err => { console.log('err1', err); // 清空大payload状态 this._store.dispatch(Action.resetBigPayload()); // 将错误传递给retryWhen处理 return throwError(() => err); }) ); }), retryWhen(error$ => { // 只重试1次,且等待bigPayload清空后再执行 return error$.pipe( take(1), switchMap(() => this._store.select(state => state.bigPayload).pipe( // 等待payload变为空或无内容 filter(payload => !payload || Object.keys(payload).length === 0), take(1) )) ); }) )
原理:错误触发后,先清空store状态,retryWhen会等待bigPayload变为空,再重新订阅上游流——此时withLatestFrom会获取到最新的空payload,执行API调用。
方案二:递归调用逻辑手动控制重试
如果你需要更灵活的重试逻辑,可以用递归方式实现,确保每次调用都获取最新的store状态:
request$.pipe( switchMap(({ request }) => { // 定义递归函数,最多重试1次 const attemptApiCall = (retryCount: number = 0) => { return this._store.select(state => ({ bigPayload: state.bigPayload, somethingElse: state.somethingElse })).pipe( take(1), switchMap(({ bigPayload, somethingElse }) => { return this._service.callApi(bigPayload).pipe( tap(resp => { // 成功逻辑处理 }), catchError(err => { if (retryCount < 1) { console.log('err1', err); this._store.dispatch(Action.resetBigPayload()); // 重试,次数+1 return attemptApiCall(retryCount + 1); } // 重试次数耗尽,抛出错误 return throwError(() => err); }) ); }) ); }; return attemptApiCall(); }) )
原理:每次调用API前都会从store获取最新的bigPayload,第一次失败后清空状态,递归调用时就会拿到空payload执行重试。
方案三:移除catchError调整错误处理位置(仅适用于热流场景)
如果request$是热Observable(比如持续发射的流),可以移除catchError,让错误直接触发retry,同时把错误处理放在tap中:
request$.pipe( withLatestFrom( this._store.select(state => state.bigPayload), this._store.select(state => state.somethingElse) ), switchMap(([{ request }, bigPayload, somethingElse]) => { return this._service.callApi(bigPayload).pipe( tap(resp => { // 成功逻辑处理 }) ); }), tap({ error: err => { console.log('err', err); this._store.dispatch(Action.resetBigPayload()); } }), retry(1) )
注意:如果request$是单次动作触发的冷流,这个方案无效——因为retry重新订阅后不会有新的发射。
内容的提问来源于stack exchange,提问作者mwilson
相关产品推荐
相关产品推荐

