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

Firebase模拟器教程第16步中Firestore onSnapshot的Promise无法resolve导致测试超时问题

这个问题我之前调试Firebase时也踩过坑,大概率是浮点数精度误差在搞鬼,再加上订阅逻辑的小瑕疵,咱们一步步拆解:

核心原因:浮点数精度坑导致条件未触发

你代码里用==直接比较totalPrice和expectedTotal,但JavaScript的浮点数运算天生存在精度缺陷。比如如果totalPrice是通过4.99 * 2计算得到的,实际底层数值是9.979999999999999,而非精确的9.98。虽然console.log会把它格式化成9.98显示,但直接用==比较时会判定不相等——这就导致resolve()根本没被调用,Promise一直处于pending状态,测试自然超时。

另外还有个小问题:第一次回调触发时,因为不满足条件你没调用unsubscribe,这个监听器会一直挂着,虽然第二次满足条件后会取消,但如果期间有其他意外快照触发,可能干扰逻辑(不过这不是超时的主因)。

修复方案

我给你调整了代码,针对性解决这些问题:

await new Promise((resolve, reject) => { 
  const unsubscribe = aliceCartRef.onSnapshot(snap => { 
    const data = snap.data();
    // 先处理数据为空的极端情况
    if (!data) {
      console.log("Cart data is null, skipping");
      return;
    }
    const expectedCount = 2; 
    const expectedTotal = 9.98; 
    console.log("Current cart data:", data); 
    // 用近似比较替代直接相等,规避浮点数精度坑
    const countMatches = data.itemCount === expectedCount;
    const priceMatches = Math.abs(data.totalPrice - expectedTotal) < 0.001;
    
    if (countMatches && priceMatches) { 
      console.log("Condition met!"); 
      unsubscribe(); // 立即取消订阅
      resolve(); // 终止Promise
      console.log("Promise resolved"); 
    }
  }, error => {
    // 处理监听过程中的错误,避免Promise挂死
    console.error("Snapshot listen error:", error);
    unsubscribe();
    reject(error);
  });

  // 加超时兜底,防止无限等待
  setTimeout(() => {
    unsubscribe();
    reject(new Error("Timeout: Cart didn't update as expected within 5s"));
  }, 5000);
});

关键修改点说明

  1. 浮点数比较优化:用Math.abs(实际值 - 期望值) < 0.001判断价格是否一致,只要差值小于1分钱(以元为单位)就认为相等,彻底规避精度问题。
  2. 错误与边界处理:增加了snap.data()为空的判断,以及onSnapshot的错误回调,确保任何异常都能及时终止Promise。
  3. 超时兜底:5秒超时机制,避免因为逻辑漏洞导致测试无限挂起。

额外建议

处理金额的最佳实践是用整数存储分单位(比如998分代替9.98元),这样完全不会有浮点数精度问题,计算和比较都能精准进行。

内容的提问来源于stack exchange,提问作者Damian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 05:48:11