使用RxJs的expand配合递归实现分页出现无限循环该如何修复?
问题根因
你遇到的无限循环问题确实来自nextPosition的作用域问题:这个变量是在retreivePagedRecords函数执行初始就计算完成的固定值,同一个函数调用上下文里的expand回调读取到的永远是这个固定值,不会随分页推进更新,自然永远触发不了终止条件。
而且你现在把expand写在递归调用的函数内部,相当于每次请求都会生成新的expand流,逻辑完全混乱了,不需要把RxJS的递归操作符和JS函数递归混在一起用。
修复方案
你可以直接用expand本身的递归能力实现分页,不需要额外写JS递归函数,调整后的代码如下:
private async retreivePagedRecords<T>( companyURL: string, query: string, startPosition: number, totalCount: number, transformFn: (qbData: any) => T[] ): Promise<T[]> { const headers = this.getHttpConfig(); // 封装单次获取单页数据的逻辑 const getPage = (currentPosition: number) => { const pageQuery = `${query} STARTPOSITION ${currentPosition} MAXRESULTS ${this.pagingSize}`; return this.http.get(`${companyURL}/query?query=${pageQuery}`, headers).pipe( map(x => (x as any).data), map(transformFn), // 把当前页的位置和数据一起返回,给expand判断下一页用 map(pageData => ({ pageData, currentPosition })) ); }; const records = await lastValueFrom( getPage(startPosition).pipe( // expand接收上一次的输出,动态计算下一页的位置 expand(({ currentPosition }) => { const nextPosition = currentPosition + this.pagingSize; return nextPosition > totalCount ? [] : getPage(nextPosition); }), // 只提取返回的数据部分做合并 map(({ pageData }) => pageData), reduce((acc: T[], x: T[]) => acc.concat(x ?? []), []), catchError(error => of([])) ) ); return records; }
修改点说明
- 把单页请求逻辑封装成独立的
getPage方法,接收当前位置作为入参,每次调用都会生成对应位置的请求 - 去掉了函数递归逻辑,完全用expand本身的能力处理分页递归,动态计算每次的
nextPosition,不会出现固定值的问题 - 单次请求返回时把当前位置和数据一起传递,确保expand能拿到上一页的真实位置计算下一页偏移
如果你一定要保留原来的函数递归写法,也可以把nextPosition作为入参透传,但完全没必要,上述写法逻辑更清晰,也符合RxJS操作符的设计意图。
内容的提问来源于stack exchange,提问作者link64
相关产品推荐
相关产品推荐

