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); });
关键修改点说明
- 浮点数比较优化:用
Math.abs(实际值 - 期望值) < 0.001判断价格是否一致,只要差值小于1分钱(以元为单位)就认为相等,彻底规避精度问题。 - 错误与边界处理:增加了
snap.data()为空的判断,以及onSnapshot的错误回调,确保任何异常都能及时终止Promise。 - 超时兜底:5秒超时机制,避免因为逻辑漏洞导致测试无限挂起。
额外建议
处理金额的最佳实践是用整数存储分单位(比如998分代替9.98元),这样完全不会有浮点数精度问题,计算和比较都能精准进行。
内容的提问来源于stack exchange,提问作者Damian
相关产品推荐
相关产品推荐

