VueJS调用Firestore时异步代码返回空数组如何解决
问题背景
开发环境
- 数据库:Firestore
- 前端框架:VueJS
开发目标
- 读取
players集合中所有可用足球运动员的列表,每名运动员对应集合内的独立文档 - 获取所有上述运动员对应的全部赛事统计数据
- 按球员维度聚合,统计每名球员所有参赛场次的累计数据
故障现象
- 页面初始化执行逻辑时,
this.aggregatedPlayerStats返回空数组 - 手动通过按钮触发
aggregatePlayerStats()方法时,聚合功能可正常运行,初步判断故障和异步代码执行时序有关
原始问题代码
export default { name: "AggregatedAllTimeStats", data() { return { allPlayers: [], allStats: [], aggregatedPlayerStats: [] }; }, async created() { await this.getAllPlayers(); await this.getAllStats(); this.aggregatePlayerStats(); }, methods: { async getAllPlayers() { return db.collection("players").get().then((querySnapshot) => { querySnapshot.forEach((doc) => { this.allPlayers.push({ playerid: doc.id, name: doc.data().name, playerstatus: doc.data().playerstatus }) }) }) }, getAllStats() { this.allPlayers.forEach((player) => { return db.collection("playerstatsformatch").where("playerid", "==", player.playerid).get().then((querySnapshot) => { querySnapshot.forEach((doc) => { this.allStats.push(doc.data()) }) }) }) }, aggregatePlayerStats() { const aggregatedResults = Object.values( this.allStats.reduce((a, c) => { if (!a[c.playerid]) { a[c.playerid] = { goals: c.goals, name: c.name, assists: c.assists }; } else { a[c.playerid].goals += c.goals; } return a; }, {}) ); this.aggregatedPlayerStats = aggregatedResults console.log(this.aggregatedPlayerStats) } }, };
故障根因
故障点在getAllStats()方法的异步逻辑处理错误:
- 方法未声明为
async,内部使用forEach遍历球员列表发起Firestore查询时,forEach本身不会等待内部异步请求执行完成,方法会在所有查询刚发起时就同步返回 created钩子中虽然写了await this.getAllStats(),但getAllStats没有返回可等待的有效Promise,await实际没有阻塞执行,会立刻运行后续的aggregatePlayerStats()方法。此时所有赛事统计数据的查询还没返回结果,this.allStats是空数组,最终聚合结果自然为空。
修复方案
修改getAllStats()方法,使用Promise.all等待所有球员的统计数据查询全部完成后再返回,确保执行聚合逻辑时所有统计数据已经写入this.allStats。
修复后的完整代码:
export default { name: "AggregatedAllTimeStats", data() { return { allPlayers: [], allStats: [], aggregatedPlayerStats: [] }; }, async created() { await this.getAllPlayers(); await this.getAllStats(); this.aggregatePlayerStats(); }, methods: { async getAllPlayers() { const querySnapshot = await db.collection("players").get(); querySnapshot.forEach((doc) => { this.allPlayers.push({ playerid: doc.id, name: doc.data().name, playerstatus: doc.data().playerstatus }) }) }, async getAllStats() { // 收集所有球员的统计数据查询Promise const statQueries = this.allPlayers.map(player => { return db.collection("playerstatsformatch") .where("playerid", "==", player.playerid) .get() }) // 等待所有查询执行完成 const allQuerySnapshots = await Promise.all(statQueries); // 统一写入所有统计数据 allQuerySnapshots.forEach(querySnapshot => { querySnapshot.forEach(doc => { this.allStats.push(doc.data()) }) }) }, aggregatePlayerStats() { const aggregatedResults = Object.values( this.allStats.reduce((a, c) => { if (!a[c.playerid]) { a[c.playerid] = { goals: c.goals, name: c.name, assists: c.assists }; } else { a[c.playerid].goals += c.goals; a[c.playerid].assists += c.assists; } return a; }, {}) ); this.aggregatedPlayerStats = aggregatedResults console.log(this.aggregatedPlayerStats) } }, };
额外修复点:原聚合逻辑只累加了进球数,漏了助攻数的累加,修复代码中已补充该逻辑。
内容的提问来源于stack exchange,提问作者Christoffer Endresen
相关产品推荐
相关产品推荐

