Firebase中for循环调用onSnapshot更新全局对象属性仅最后一项生效
问题核心原因
- 循环变量的作用域绑定错误:你在
for...in循环中没有用块级作用域关键字let声明循环变量sc,导致所有异步回调捕获的是同一个sc变量的引用。onSnapshot是异步触发的回调,等到回调实际执行时,同步的循环已经跑完,sc已经被赋值为supportedCommodities最后一个属性的键,所以所有回调最终修改的都是最后一个大宗商品的value属性。 - 额外语义问题:
onSnapshot是Firebase提供的实时监听器,会持续监听数据变更并反复触发回调,如果你的需求只是单次拉取数据更新全局对象,用onSnapshot会导致不必要的监听器泄漏,后续数据变更会反复修改你的全局属性。
修复方案
只需要给循环变量加let声明保证块级作用域绑定即可,如需单次查询可将onSnapshot替换为get:
let supportedCommodities = { SILVER: {name: "silver", value: 0}, GOLD: {name: "gold", value: 0} } async function updateCommodities() { // 用let声明循环变量sc,保证每次循环都生成独立的块级作用域绑定 for (let sc in supportedCommodities) { let commodityName = supportedCommodities[sc]["name"]; // 单次查询替换为get(),如需实时监听保留onSnapshot即可 const querySnapshot = await firebaseDB.collection("commodities").where("name", "==", commodityName).get(); if (querySnapshot.docs.length != 0) { const commodityData = querySnapshot.docs[0].data(); supportedCommodities[sc]["value"] = await getCommodityValue(commodityData.ounces, commodityData.name); } } }
如果确实需要保留实时监听的能力,仅给sc加let声明即可解决作用域问题:
async function updateCommodities() { for (let sc in supportedCommodities) { let commodityName = supportedCommodities[sc]["name"]; firebaseDB.collection("commodities").where("name", "==", commodityName) .onSnapshot(async (querySnapshot) => { if (querySnapshot.docs.length != 0) { const commodityData = querySnapshot.docs[0].data(); supportedCommodities[sc]["value"] = await getCommodityValue(commodityData.ounces, commodityData.name); } }); } }
内容的提问来源于stack exchange,提问作者user5824608
相关产品推荐
相关产品推荐

