如何使用RxJs扁平化响应?能否避免嵌套concatMap操作?
你的代码逻辑是按顺序遍历URL数组,逐个调用API获取包含items数组的响应,再将每个响应的items元素逐个展开,最终合并成一个完整数组输出。确实可以通过调整操作符组合避免嵌套concatMap,或者用更贴合需求的写法:
方案1:拆分concatMap与concatAll,消除嵌套
先通过concatMap发起请求并提取每个响应的items数组,再用*concatAll()*将这些数组展开为单个元素的流,最后用toArray()合并:
from(["url_1", "url_2"]) .pipe( concatMap(url => this.vocabularyService.getSimilarProducts(url).pipe(map(res => res.items))), concatAll(), toArray() ) .subscribe(res => console.log(res));
这里map(res => res.items)先提取每个API响应的items数组,此时流的类型是Observable<Object[]>;*concatAll()*会把每个数组转化为同步发出元素的Observable,并按顺序展开所有元素;最后toArray()将所有元素收集为一个大数组。
方案2:用from()配合mergeMap展开数组
如果不想用concatAll,也可以在请求后用from()把items数组转成Observable,再通过*mergeMap()*展开,写法上更直观,且避免深层嵌套:
from(["url_1", "url_2"]) .pipe( concatMap(url => this.vocabularyService.getSimilarProducts(url).pipe( map(res => res.items), mergeMap(items => from(items)) ) ), toArray() ) .subscribe(res => console.log(res));
from(items)会把数组转化为逐个发出元素的Observable,mergeMap()(处理同步流时和concatMap效果一致)负责将这个流展开到外层流中。
方案3:拆分操作符,最简洁写法
直接把两个嵌套的concatMap拆成两个独立的操作符,逻辑更清晰:
from(["url_1", "url_2"]) .pipe( concatMap(url => this.vocabularyService.getSimilarProducts(url)), mergeMap(res => res.items), toArray() ) .subscribe(res => console.log(res));
第一个concatMap负责按顺序发起所有API请求,第二个*mergeMap()*负责把每个响应的items数组展开为单个元素;如果需要严格保持请求和元素的顺序一致性,把第二个mergeMap换成concatMap也可以,处理同步数组时两者结果无差异。
RxJS没有专门的单一操作符直接完成“请求+提取数组展开+合并”的全流程,但通过上述操作符组合,完全可以避免嵌套concatMap,让代码结构更易读。
内容的提问来源于stack exchange,提问作者JackFord

