Angular递归调用API时请求被取消无报错的解决方案咨询
Angular递归调用API请求被取消的解决办法
问题场景
我在Angular里用服务实现addItems功能,逻辑是当API返回的所有结果状态都是success时,就递归调用addItems。但实际运行时,递归的请求直接被取消,既没报错,也看不到网络请求。需要在每个请求返回后执行对应操作,求RxJS或Angular的可行方案(比如延迟调用)。
原始代码:
addItems() { this.addservice .addToCart(this.currentBatch, this.batch) .pipe( concatMap((res) => { if (res) { const success = res.filter( (item) => !!(item.statusCode === 'success') ); if(res.length === success.length){this.addItems()} } }) ).subscribe(res => console.log(res) ); }
问题根源
直接在concatMap里同步调用this.addItems()会导致新订阅和旧订阅冲突,而且这种手动递归的方式没有遵循RxJS的流控规则,请求的生命周期和流的执行时机不匹配,最终导致请求被取消。
可行解决方案
方案一:用RxJS expand操作符实现递归流
expand是RxJS专门用来处理递归流的操作符,它会在每个Observable完成后,根据条件递归发出新的Observable,完全基于流的机制处理,避免手动调用的问题。还可以加延迟避免请求过于频繁。
修改后的代码:
import { EMPTY, timer } from 'rxjs'; import { expand, tap, catchError } from 'rxjs/operators'; addItems() { this.addservice.addToCart(this.currentBatch, this.batch) .pipe( // 处理当前请求返回后的操作 tap(res => { console.log(res); // 这里可以加你需要的其他操作,比如更新UI、记录日志等 }), // 递归判断:是否发起下一次请求 expand(res => { if (!res) { return EMPTY; // 无返回结果时终止递归 } const successItems = res.filter(item => item.statusCode === 'success'); if (res.length === successItems.length) { // 延迟1秒再发起下一次请求,可根据需求调整时长 return timer(1000).pipe( switchMap(() => this.addservice.addToCart(this.currentBatch, this.batch)) ); } else { return EMPTY; // 有失败项时终止递归 } }), // 可选:限制递归次数,防止无限循环(比如最多调用10次) // take(10), // 捕获请求错误,避免单个请求失败导致整个流中断 catchError(err => { console.error('请求出错:', err); return EMPTY; }) ).subscribe(); }
方案二:用concatMap+defer延迟递归调用
如果不想用expand,也可以把递归逻辑包装成Observable,用concatMap串联起来,同时用defer延迟创建新的Observable,避免同步冲突。
代码示例:
import { EMPTY, timer, defer } from 'rxjs'; import { concatMap, tap, catchError } from 'rxjs/operators'; addItems() { const processRequest = () => this.addservice.addToCart(this.currentBatch, this.batch) .pipe( tap(res => { console.log(res); // 执行当前请求后的操作 }), concatMap(res => { if (!res) return EMPTY; const successItems = res.filter(item => item.statusCode === 'success'); if (res.length === successItems.length) { // 延迟500毫秒后递归调用 return timer(500).pipe(switchMap(() => defer(() => processRequest()))); } return EMPTY; }), catchError(err => { console.error('请求出错:', err); return EMPTY; }) ); processRequest().subscribe(); }
关键注意点
- 绝对不要在操作符或订阅回调里同步调用递归方法,要通过RxJS的流操作符来管理递归逻辑
- 添加延迟(
timer)不仅能避免请求过于频繁,还能让浏览器和服务器有足够的处理时间,减少异常 - 必须添加
catchError处理错误,防止单个请求失败导致整个递归流中断 - 建议用
take操作符限制递归次数,避免API一直返回全成功时出现无限循环
内容的提问来源于stack exchange,提问作者Mohammed Rabiulla RABI
相关产品推荐
相关产品推荐

