Angular+Firestore联系人数据获取问题及RxJS操作符困惑
问题:Angular结合Firestore获取联系人数据报错及解决方案
我对Angular的map类操作符理解不足,观看数小时视频教程仍未解决问题。场景为使用Firestore存储用户数据,每个用户拥有一个存储其他用户ID的数组,需要获取每个ID对应的用户数据并存入contacts数组。尝试通过contactData$ Observable基于当前用户数据创建流,但出现报错。
原代码
getFbContactData方法(构造函数中加载)
getFbContactData() { this.contacts = []; this.activeUsersDoc.get().pipe(switchMap(userSnapshot => { return from<string[]>(userSnapshot.data()['contacts']); })).pipe(concatMap((contactId) => { this.contactUsersDoc = this.usersCollection.doc(contactId); this.getContactData(contactId); return this.contactData$ })).pipe(switchMap(() => this.contactData$) ).subscribe((contact) => { }) }
getContactData方法
getContactData(userId) { const contactUserDoc = this.usersCollection.doc(userId); contactUserDoc.get().pipe(map(userSnapshot => { return userSnapshot })).subscribe((userSnapshot) => { const contact: Contact = new Contact( { uid: userSnapshot.data()['uid'], firstName: userSnapshot.data()['firstName'], lastName: userSnapshot.data()['lastName'], initials: userSnapshot.data()['initials'], email: userSnapshot.data()['email'], displayName: userSnapshot.data()['displayName'], } ) console.log(contact); this.contactData$ = of(contact); }) }
报错信息
core.mjs:8400 ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable. at createInvalidObservableTypeError (throwUnobservableError.js:2:12) at innerFrom (innerFrom.js:37:43) at doInnerSub (mergeInternals.js:19:18) at outerNext (mergeInternals.js:14:57) at OperatorSubscriber._next (OperatorSubscriber.js:13:21) at OperatorSubscriber.next (Subscriber.js:31:18) at subscribe.innerSubscriber (switchMap.js:14:144) at OperatorSubscriber._next (OperatorSubscriber.js:13:21) at OperatorSubscriber.next (Subscriber.js:31:18) at Observable._subscribe (innerFrom.js:51:24)
解决方案
报错核心原因
contactData$在concatMap中返回时处于undefined状态:getContactData是异步操作,调用后立刻返回contactData$,此时它还未被赋值。- 手动订阅
getContactData内的Observable破坏了流的链式结构,导致异步时序混乱。
重构后的代码
主方法getFbContactData
getFbContactData() { this.activeUsersDoc.get().pipe( // 获取当前用户的联系人ID数组 switchMap(userSnapshot => { const contactIds = userSnapshot.data()?.['contacts'] as string[]; // 处理无联系人的边界情况 if (!contactIds || contactIds.length === 0) { return of([]); } // 将数组转为Observable流,逐个处理ID return from(contactIds); }), // 按顺序逐个获取联系人数据(保持顺序) concatMap(contactId => this.getContactData(contactId)), // 将所有联系人数据收集为一个数组 toArray() ).subscribe({ next: (contacts) => { this.contacts = contacts; console.log('已获取联系人列表:', contacts); }, error: (err) => { console.error('获取联系人失败:', err); } }); }
联系人数据获取方法getContactData
// 改为返回Observable<Contact>,不再手动订阅 getContactData(userId: string): Observable<Contact> { const contactUserDoc = this.usersCollection.doc(userId); return contactUserDoc.get().pipe( map(userSnapshot => { const userData = userSnapshot.data(); // 处理用户不存在的情况 if (!userData) { throw new Error(`ID为${userId}的用户不存在`); } return new Contact({ uid: userData['uid'], firstName: userData['firstName'], lastName: userData['lastName'], initials: userData['initials'], email: userData['email'], displayName: userData['displayName'], }); }) ); }
优化说明
- 移除手动订阅:
getContactData返回Observable,保持流的链式调用,避免异步时序问题。 - 边界情况处理:增加了无联系人、用户数据为空的错误处理,提升代码健壮性。
- 并行获取优化:如果不需要保持联系人顺序,可将
from(contactIds)+concatMap替换为forkJoin实现并行获取,效率更高:switchMap(userSnapshot => { const contactIds = userSnapshot.data()?.['contacts'] as string[]; if (!contactIds || contactIds.length === 0) { return of([]); } // 并行获取所有联系人数据 const contactObservables = contactIds.map(id => this.getContactData(id)); return forkJoin(contactObservables); })
内容的提问来源于stack exchange,提问作者IngoHe
相关产品推荐
相关产品推荐

