Angular Async Pipe 首次加载Firebase数据仅显示部分内容异常问题
问题根因分析
- 异步操作时序错误:你在遍历完
user.ownedProjects后直接调用observer.next(observableProjects),但循环内的onSnapshot监听、Firestore描述集合查询都是异步IO操作,不会阻塞同步代码执行。首次调用next时,只有第一个完成的异步请求把项目push进了数组,剩下的项目后续才会插入数组,但不会再触发新的next,async pipe拿不到更新后的数据。 - 时序巧合导致刷新正常:下拉刷新时你加了500ms的延迟,刚好足够两个项目的所有异步请求执行完成,重新订阅Observable时拿到的
observableProjects已经是填充完成的数组,所以能显示全部项目,这是不可靠的巧合逻辑,网络慢的时候依然会出问题。 - 隐藏的内存泄漏问题:你每次调用
refreshProjects都会新建Observable、新建userObject订阅、新建多个onSnapshot监听,但从未取消旧的订阅,重复调用会导致内存泄漏、重复触发数据更新。 - 异步逻辑写法混乱:混合使用嵌套subscribe、async/await、Promise.then,没有统一用RxJS操作符管理异步流,导致时序难以控制。
修复方案
建议用RxJS操作符重构逻辑,避免手动创建Observable和嵌套订阅,等所有项目的异步请求都完成后再统一抛出结果:
import { switchMap, combineLatest, map, from } from 'rxjs/operators'; refreshProjects = function(event) { // 用switchMap切换用户流到项目流,自动取消旧订阅 this.projects = this.userObject.pipe( switchMap(user => { // 给每个项目ID创建对应的项目数据查询流 const projectStreams = user.ownedProjects.map(projectID => // 用from把Promise转为Observable,不需要实时监听的场景用get()更合理 from(this.firestore.collection('projects').doc(projectID).ref.get()).pipe( switchMap(async doc => { if (!doc.exists) return null; const tempProject = doc.data() as Project; const tempDate = tempProject.startDate as unknown as { nanoseconds: number; seconds: number; }; const tempLocation = tempProject.location as unknown as string; const observerProject: Project = new Project( tempProject.fullName, tempProject.shortName, tempProject.ownerID, tempProject.lastOwnerID, tempDate.seconds, tempProject.duration, tempProject.logo, tempProject.status, tempProject.categoryID, tempLocation, [], tempProject.timelinePosts ); // 查询描述集合 const descSnapshot = await this.firestore.collection('projects/' + projectID + '/descriptions').ref.get(); descSnapshot.forEach(doc => { observerProject.description.push(doc.data() as Description); }); return observerProject; }) ) ); // 等待所有项目流返回结果,过滤空值 return combineLatest(projectStreams).pipe( map(projects => projects.filter(p => p != null)) ); }) ); if (event) { this.showProjects = false; setTimeout(() => { this.showProjects = true; event.target.complete(); }, 500); } }
如果需要保留项目数据实时监听的能力,把ref.get()换回onSnapshot,用fromEventPattern转换为Observable即可。
内容的提问来源于stack exchange,提问作者Timur-O
相关产品推荐
相关产品推荐

