查询仅存在于单张表中的用户,最佳RxJS操作符选择咨询
兄弟,我懂你遇到的问题了——用forkJoin的时候四个请求报错,哪怕加了catchError返回EMPTY还是拿不到结果,这事儿确实坑。咱们一步步拆解,先搞清楚为啥之前的方案不行,再给你几个靠谱的替代方案。
为啥你之前的forkJoin方案没用?
你给每个Observable加catchError(() => EMPTY),看起来合理,但forkJoin有个硬规则:必须所有源Observable都至少发射一个值,并且全部完成,才会输出结果数组。而EMPTY是个啥?它是直接完成但不发射任何值的Observable,四个报错的请求都变成了EMPTY,等于forkJoin一直在等这四个Observable发射值,但它们永远不会发,最终导致forkJoin根本不会输出任何结果,更别说拿到那个成功的用户数据了。
方案一:用forkJoin,但把EMPTY换成发射null的Observable
如果还是想用forkJoin(比如你需要等所有请求都完成再统一处理),那把catchError里的EMPTY改成of(null)或者of(undefined)。这样每个报错的请求会发射一个null然后完成,成功的那个发射用户数据,forkJoin就能正常输出数组了,之后你只要从数组里过滤出非null的结果就行:
forkJoin([ getUserTable1$.pipe(catchError(() => of(null))), getUserTable2$.pipe(catchError(() => of(null))), getUserTable3$.pipe(catchError(() => of(null))), getUserTable4$.pipe(catchError(() => of(null))), getUserTable5$.pipe(catchError(() => of(null))), ]).subscribe(results => { const user = results.find(item => item !== null); if (user) { console.log('找到目标用户:', user); } else { console.log('所有表都没找到用户'); } });
方案二:用merge+first,高效获取第一个成功的结果
如果你不想等所有请求都完成,只要拿到第一个成功的结果就够(毕竟用户只在一张表里),那merge操作符更适合你。merge会同时订阅所有Observable,只要有一个发射值就会立刻传递下去,配合catchError把报错的转化为EMPTY(不发射值),再用first拿到第一个有效结果后就停止订阅,效率拉满:
merge( getUserTable1$.pipe(catchError(() => EMPTY)), getUserTable2$.pipe(catchError(() => EMPTY)), getUserTable3$.pipe(catchError(() => EMPTY)), getUserTable4$.pipe(catchError(() => EMPTY)), getUserTable5$.pipe(catchError(() => EMPTY)) ).pipe(first()) // 拿到第一个结果就自动停止订阅 .subscribe(user => { console.log('找到目标用户:', user); }, error => { // 只有当所有请求都报错时才会走到这里 console.log('所有表都没找到用户'); });
这个方案的优势是快,只要第一个成功的请求返回,你就能立刻拿到数据,不用等其他四个报错的请求完成,而且first会自动取消订阅剩下的请求(如果你的Observable支持取消的话,比如HTTP请求通常都支持),避免不必要的资源浪费。
方案三:用concat按顺序查询(适合不想同时发起多请求的场景)
如果你的场景不允许同时发起五个请求,想按顺序查,直到找到用户为止,那用concat+first也可以:
concat( getUserTable1$.pipe(catchError(() => EMPTY)), getUserTable2$.pipe(catchError(() => EMPTY)), getUserTable3$.pipe(catchError(() => EMPTY)), getUserTable4$.pipe(catchError(() => EMPTY)), getUserTable5$.pipe(catchError(() => EMPTY)) ).pipe(first()) .subscribe(user => { console.log('找到目标用户:', user); });
这个会先查第一张表,失败了再查第二张,以此类推,直到找到用户为止。
总结一下
- 要等所有请求完成再处理:选
forkJoin+of(null) - 要最快拿到结果:选
merge+first - 要按顺序查询:选
concat+first
备注:内容来源于stack exchange,提问作者Sakén

