You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 14:27:05