Angular遍历Observable返回数组并拉取嵌套物料数据的实现问题
问题原因
你当前代码的问题出在第一层操作符用了map,处理后返回的是「每个item对应的Observable组成的数组」,最终返回的流类型是Observable<Observable<any>[]>,外层没有订阅内层的这些物料请求Observable,所以无法得到最终的拼接后数据。
修复后的代码
import { forkJoin, switchMap } from 'rxjs'; // 提前定义类型,避免用any interface Material { name: string; img: string; } interface Item { name: string; materials: number[] | Material[]; } public getAllItemsData(): Observable<Item[]> { return getItems().pipe( // 用switchMap替换map,承接返回新的Observable流 switchMap((list: Item[]) => { // 遍历每个item,生成对应的带物料详情的Observable const itemObservables = list.map(item => { // 批量生成当前item的所有物料请求 const matObservables = item.materials.map((id: number) => getMaterial(id)); return forkJoin(matObservables).pipe( map(materialDetails => ({ ...item, materials: materialDetails })) ); }); // 外层再包一层forkJoin,等所有item的物料请求完成后统一返回完整列表 return forkJoin(itemObservables); }) ); }
代码说明
- 无特殊顺序要求的场景下,
switchMap也可以替换为mergeMap/concatMap,核心作用是承接上游流的结果,返回一个新的Observable流而非普通值 - 内层先对单个item的物料ID数组生成
getMaterial请求集合,用forkJoin等待当前item的所有物料请求完成后替换materials字段 - 外层对所有item对应的Observable集合再包一层
forkJoin,等所有item的物料都请求完成后,返回完整的拼接好的物品列表
可选优化方案
如果多个item存在相同的物料ID,上面的写法会重复请求相同的物料接口。你可以先把所有item的物料ID做去重,批量请求完所有物料后再映射到对应的item上,减少不必要的接口调用:
public getAllItemsData(): Observable<Item[]> { return getItems().pipe( switchMap((list: Item[]) => { // 收集所有物料ID并去重 const allMatIds = [...new Set(list.flatMap(item => item.materials as number[]))]; // 批量请求所有物料,存为ID到详情的映射表 return forkJoin(allMatIds.map(id => getMaterial(id))).pipe( map(materials => { const matMap = new Map<number, Material>( materials.map((mat, index) => [allMatIds[index], mat]) ); // 映射回原item列表 return list.map(item => ({ ...item, materials: (item.materials as number[]).map(id => matMap.get(id)!) })) }) ) }) ) }
内容的提问来源于stack exchange,提问作者Imad El Hitti
相关产品推荐
相关产品推荐

