如何等待未知数量的Observable依次完成(分页API场景)
解决未知页数的串行分页数据获取问题
问题根源
你原来的代码失效是因为同步while循环不等待异步HTTP请求完成:循环会瞬间反复执行,而url要等到HTTP请求的回调触发才会被更新,导致在url变为null前,已经发起了海量请求,直接卡死浏览器。
正确实现方案
利用RxJS的expand操作符可以完美解决这个问题——它专门处理递归式的异步流,能保证当前页请求完成后再判断是否发起下一页请求,完全串行执行。
完整代码实现
import { EMPTY, Observable } from 'rxjs'; import { expand, reduce } from 'rxjs/operators'; // 分页结果接口 interface PaginatedResult<T> { count: number; next: string | null; prev: string | null; results: T[]; } /** * 串行获取所有分页数据 * @param initialUrl 初始请求地址 * @returns 包含所有数据的Observable */ function fetchAllData<T>(initialUrl: string): Observable<T[]> { return this.http.get<PaginatedResult<T>>(initialUrl).pipe( // 递归展开分页流:有next地址则继续请求,否则终止 expand(page => page.next ? this.http.get<PaginatedResult<T>>(page.next) : EMPTY), // 将所有页的结果合并为一个数组 reduce((allData, page) => [...allData, ...page.results], [] as T[]) ); } // 使用示例 fetchAllData<Data>(API_ENDPOINT).subscribe(allData => { console.log('全部数据已获取:', allData); // 这里处理完整数据 });
增量获取(实时更新UI)
如果需要边获取数据边更新UI,把reduce换成scan即可,每次获取一页就返回当前累计的数据:
import { EMPTY, Observable } from 'rxjs'; import { expand, scan } from 'rxjs/operators'; function fetchAllDataIncrementally<T>(initialUrl: string): Observable<T[]> { return this.http.get<PaginatedResult<T>>(initialUrl).pipe( expand(page => page.next ? this.http.get<PaginatedResult<T>>(page.next) : EMPTY), scan((allData, page) => [...allData, ...page.results], [] as T[]) ); } // 使用示例 fetchAllDataIncrementally<Data>(API_ENDPOINT).subscribe(currentData => { console.log('当前已获取的数据:', currentData); // 实时更新UI,比如渲染列表 });
代码说明
expand:接收每个分页请求的返回结果,根据next字段决定是否发起下一页请求,返回EMPTY时终止递归,确保请求严格串行。reduce:将所有分页的results数组合并成一个完整数组,最终一次性返回所有数据。scan:与reduce类似,但会在每一页请求完成后就返回当前累计的数据,适合实时更新场景。
内容的提问来源于stack exchange,提问作者Ciro García
相关产品推荐
相关产品推荐

