使用Geofire结合Firebase实时数据库时出现调用栈溢出错误求助
问题:Firebase实时数据库结合Geofire出现调用栈溢出错误
错误信息
RangeError: Maximum call stack size exceeded at ChildrenNode.isLeafNode (api/node_modules/firebase-admin/node_modules/@firebase/database-compat/dist/index.standalone.js:8956:50) at ChildrenNode.equals (api/node_modules/geofire/dist/geofire/index.cjs.js:8461:24)
原示例代码
while (some amount) { geoDriverId = await(new Promise((resolve, reject) => { try { geoDriverId = [] geoDriverIds = [] const geoQuery = config.firebase.table.query({ center: [parseFloat(pick_up_latitude), parseFloat(pick_up_longitude)], radius: i }) let listener = geoQuery.on("key_entered", (key, location, distance) => { geoDriverIds.push({ key, distance }); }); geoQuery.on("ready", (key, location, distance) => { listener.cancel(); geoQuery.cancel(); resolve(geoDriverIds); return; }); } catch (e) { console.log("e", e) } })) }
错误原因分析
- 循环内重复创建查询且清理不彻底:while循环中每次迭代都新建GeoQuery和监听器,尽管在
ready回调中执行了cancel操作,但异步清理可能存在延迟,旧的监听器仍可能触发,导致Geofire内部递归比较逻辑不断执行,最终栈溢出。 - Geofire内部递归逻辑异常:
ChildrenNode.equals方法用于比较数据节点,当数据库中存在异常数据结构(如循环引用、不符合Geofire规范的嵌套节点)时,递归比较无法终止,引发栈溢出。 - 变量作用域混乱:
geoDriverIds未在Promise内部声明为局部变量,循环迭代时可能被闭包意外引用,导致回调逻辑重复触发。
解决方案
1. 优化循环与查询清理逻辑
避免在循环内重复创建查询,确保每次查询完成后彻底清理所有监听器,防止资源泄漏和重复回调。
2. 修复变量作用域问题
在Promise内部声明局部变量存储结果,避免外部变量污染。
3. 检查数据库数据结构
确保Firebase中存储的Geofire数据符合规范:每个位置节点需为{g: 地理哈希字符串, l: [纬度, 经度]}格式,不存在循环引用或异常嵌套节点。
4. 升级依赖版本
检查firebase-admin和geofire的版本兼容性,升级至最新稳定版,官方可能已修复递归栈溢出的已知bug。
修改后的示例代码
let currentRadius = 1; // 初始搜索半径 const maxRadius = 10; // 最大搜索半径 let nearbyDrivers = []; // 仅在未找到数据且未达最大半径时继续搜索 while (nearbyDrivers.length === 0 && currentRadius <= maxRadius) { nearbyDrivers = await new Promise((resolve, reject) => { try { const drivers = []; const geoQuery = config.firebase.table.query({ center: [parseFloat(pick_up_latitude), parseFloat(pick_up_longitude)], radius: currentRadius }); // 监听进入范围的节点 const keyEnteredListener = geoQuery.on("key_entered", (key, location, distance) => { drivers.push({ key, distance }); }); // 查询完成时清理并返回结果 const readyListener = geoQuery.on("ready", () => { keyEnteredListener.cancel(); readyListener.cancel(); geoQuery.cancel(); resolve(drivers); }); // 捕获查询错误 geoQuery.on("error", (error) => { keyEnteredListener.cancel(); readyListener.cancel(); geoQuery.cancel(); reject(error); }); } catch (err) { console.error("附近司机查询失败:", err); reject(err); } }); currentRadius++; // 扩大半径继续搜索 }
内容的提问来源于stack exchange,提问作者Vasu Rangpariya
相关产品推荐
相关产品推荐

