如何等待this.user解析完成后再调用数据库获取loads集合?
解决异步用户数据加载完成前请求集合的报错问题
这是个典型的RxJS异步流时序问题——你分开订阅了currentUser和基于status$的loads$,但currentUser的异步回调还没拿到有效用户数据时,loads$可能已经开始执行数据库查询了,这时候this.user.carrier自然是null,就会报错。
我给你两个常用的解决方案,都是利用RxJS的操作符来保证数据流的顺序:
方案一:组合用户流和状态流(推荐)
这种方法会把两个流组合起来,确保只有拿到有效用户后才会触发loads的查询,而且后续用户或状态更新时,都会自动重新查询:
// 直接组合currentUser和status$的流,替代分开的订阅 this.loads$ = this.userService.currentUser.pipe( // 过滤掉未初始化的用户,确保user和carrier都存在 filter(user => !!user && !!user.carrier), // 拿到有效用户后,切换到status$的流来查询loads switchMap(user => { this.user = user; // 可选:把用户存到组件变量里供其他地方使用 return this.status$.pipe( switchMap(status => afs.collection('loads', ref => ref .where('carrierId', '==', user.carrier.id) .where('status', '==', status) ).valueChanges()) ); }) ); // 别忘了初始化status$ this.status$ = new BehaviorSubject("Active");
这里的filter是关键,它会拦截所有user或user.carrier为null/undefined的情况,只有当用户数据完全就绪时,才会进入后续的查询逻辑。
方案二:先等待用户加载完成再初始化loads$
如果你的用户数据只需要加载一次(比如用户登录后不会切换),可以先等currentUser拿到有效数据后,再创建loads$的流:
this.status$ = new BehaviorSubject("Active"); // 先订阅currentUser,拿到有效用户后再初始化loads$ this.userService.currentUser.pipe( filter(user => !!user && !!user.carrier), take(1) // 只取第一次有效用户,之后自动取消订阅 ).subscribe(user => { this.user = user; // 现在用户已就绪,创建loads$流 this.loads$ = this.status$.pipe( switchMap(status => afs.collection('loads', ref => ref .where('carrierId', '==', user.carrier.id) .where('status', '==', status) ).valueChanges()) ); });
额外提醒
- 如果你发现
currentUser会被多次订阅导致重复请求数据库,可以在userService里给currentUser加上shareReplay(1),让多个订阅共享同一个数据流:// 在userService中的currentUser定义 currentUser = this.auth.user.pipe( switchMap(user => this.afs.doc(`users/${user.uid}`).valueChanges()), shareReplay(1) ); - 尽量避免分开订阅多个Observable后依赖共享变量(比如
this.user),这种方式很容易出现时序问题,用RxJS操作符组合流是更优雅的解决方案。
内容的提问来源于stack exchange,提问作者Noober
相关产品推荐
相关产品推荐

