RxJS技术咨询:为何下述代码返回Observable<Observable<any[]>>而非预期的Observable<any[]>?
为什么你的代码返回
Observable<Observable<any[]>>而不是Observable<any[]>? 嘿,刚接触RxJS确实容易踩这类操作符的坑,我来给你拆解一下问题所在:
问题根源
你的代码里有两个关键问题导致了嵌套Observable:
- 用了
map而不是能切换Observable的操作符:在getSubordinates的pipe里,你用map处理返回的下属列表,然后直接返回了一个Observable<any>[](也就是obsArr)。map操作符的作用是把上游的值做同步转换后直接发射,所以这里外层的Observable会把这个Observable[]作为值发射出去,最终就变成了Observable<Observable<any>[]>(和你说的Observable<Observable<any[]>>本质是一样的嵌套结构)。 - 没有合并内部的Observable数组:你为每个联系人详情创建了单独的Observable,但没有把这些Observable合并成一个能发射最终结果数组的Observable,而是直接把数组返回了。
修复方案
我们需要做两个关键调整:
- 把处理下属列表的
map换成switchMap(它能把上游的值转换成新的Observable,然后订阅这个新Observable并发射它的结果) - 用
forkJoin来合并所有联系人详情的Observable(forkJoin会等待所有Observable都完成,然后把它们的结果按顺序打包成一个数组发射)
另外注意你的代码里有几处变量拼写错误:比如if (contacts.length)应该是if (subs.length),还有contact[0].EntityId和后面的contact.EntityId可能存在变量不一致的问题,我也一并帮你修正了。
修改后的完整代码:
import { forkJoin } from 'rxjs'; // 别忘了引入forkJoin getResults() { // first endpoint to get the logged-in contact's id return this.getContact() .pipe( switchMap((contact: any) => { // second endpoint to get a contact's subordinates return this.getSubordinates(contact[0].EntityId) .pipe( switchMap((subs: any[]) => { // 把map换成switchMap // if contact has subs, it means he or she are a supervisor if (subs.length) { // 修正变量名:contacts -> subs // get details of subs const obsArr = subs.map(c => { // 修正变量名:contacts -> subs // third endpoint to get a contact's details return this.getDetailsOfContact(c.EntityId).pipe( map(details => ({ details , c })) ); }) // also get details of supervisor itself const supervisorObs = this.getDetailsOfContact(contact[0].EntityId).pipe( // 修正:contact -> contact[0],和前面的EntityId来源一致 map(details=> ( { details, c: { contactid: contact[0].Attributes.contactid, fullname: contact[0].Attributes.fullname } } ) ) ) obsArr.push(supervisorObs) return forkJoin(obsArr); // 用forkJoin合并Observable数组 } else { // if contact does not have any subordinates // it means they are not a supervisor // then get details for supervisor only const supervisorObs = this.getDetailsOfContact(contact[0].EntityId).pipe( map(details => ( { details, c: { contactid: contact[0].Attributes.contactid, fullname: contact[0].Attributes.fullname } } ) ) ); return forkJoin([supervisorObs]); // 同样用forkJoin包装成数组发射 } }) ) ); }) }
为什么这样能解决问题?
switchMap替代map后,会把我们创建的forkJoin(obsArr)这个Observable作为新的流,外层的Observable会订阅这个流,最终发射的是forkJoin返回的结果数组,也就是any[]。forkJoin把多个独立的联系人详情请求Observable合并成一个,等所有请求都完成后,一次性发射包含所有详情的数组,正好符合你期望的“发射联系人数组”的需求。
内容的提问来源于stack exchange,提问作者Jadenkun
相关产品推荐
相关产品推荐

