如何用RxJS替代递归setTimeout实现上传状态查询请求的重试?
用RxJS实现轮询获取文件上传状态
当然可以用RxJS的操作符实现这种轮询逻辑,相比递归setTimeout,RxJS的方案更符合响应式编程思想,代码更简洁可控,也更容易管理订阅生命周期。
下面是重构后的实现代码:
checkImportStatus(): void { this.restaurantService.getImportStatus().pipe( // 递归处理轮询:若响应非终态,延迟3秒后再次发起请求 expand(res => { if (res.result.status !== 'success' && res.result.status !== 'error') { return timer(3000).pipe(switchMap(() => this.restaurantService.getImportStatus())); } // 终态时结束流 return EMPTY; }), // 组件销毁时终止订阅,避免内存泄漏 takeUntil(this.destroy$), // 仅获取最后一个终态响应 last() ).subscribe({ next: (res) => { this.spinner.hide(); if (res.result.status === 'error') { this.toastr.error(res.result.restaurant.message, 'Error!'); } else { this.toastr.success(res.result.message, 'Success'); } }, error: (err) => { console.error(err); this.spinner.hide(); this.toastr.error('获取上传状态失败', '错误'); } }); }
关键操作符说明
expand:对源Observable发出的每个值返回新的Observable,实现递归轮询逻辑。当响应不是success或error时,返回延迟3秒后的新请求;终态时返回EMPTY结束流。timer(3000):替代setTimeout实现3秒延迟,符合RxJS的响应式风格。switchMap:将延迟事件映射为新的状态请求。last():过滤掉中间的非终态响应,只保留最后一个终态结果用于处理。takeUntil(this.destroy$):和原逻辑保持一致,确保组件销毁时取消订阅,避免内存泄漏。
扩展优化(可选)
如果需要添加最大重试次数或超时限制(比如你提到的4分钟耗时上限),可以直接在管道中添加对应操作符:
checkImportStatus(): void { this.restaurantService.getImportStatus().pipe( expand((res, index) => { // 限制最多重试20次(对应21次请求) if (index >= 20) return EMPTY; if (res.result.status !== 'success' && res.result.status !== 'error') { return timer(3000).pipe(switchMap(() => this.restaurantService.getImportStatus())); } return EMPTY; }), // 设置4分钟超时 timeout(4 * 60 * 1000), takeUntil(this.destroy$), last() ).subscribe({ next: (res) => { this.spinner.hide(); if (res.result.status === 'error') { this.toastr.error(res.result.restaurant.message, 'Error!'); } else { this.toastr.success(res.result.message, 'Success'); } }, error: (err) => { console.error(err); this.spinner.hide(); if (err.name === 'TimeoutError') { this.toastr.error('请求超时,请稍后重试', '超时'); } else { this.toastr.error('获取上传状态失败', '错误'); } } }); }
内容的提问来源于stack exchange,提问作者RAHUL KUNDU
相关产品推荐
相关产品推荐

