Node.js调用币安API实现本地订单簿时报Cannot read property '0'错误
错误根因
- 删除逻辑循环对象写错:
sort_array函数中,当array2[x][1]为0需要删除对应价格档位时,错误遍历了array2的索引i,却去访问array1[i]。当array2长度大于array1时,array1[i]为undefined,访问[0]属性就触发报错。 - 数组遍历过程中元素被提前删除:即便循环对象写对,用
for in遍历数组的同时调用splice删除元素,会导致数组长度动态变化,后续遍历到的索引对应的元素可能已经被移除,变为undefined。 - 初始化时序错乱:程序运行初期订单簿快照还未请求完成,
asks/bids可能还未初始化,或者状态切换逻辑有误,导致未完成初始化的数组被传入sort_array函数。
修复方案
1. 重写sort_array函数
function sort_array(array1, array2, type = 'asks') { // 先校验传入数组合法性,避免非法输入报错 if (!Array.isArray(array1) || !Array.isArray(array2)) return array1 || []; for (const updateItem of array2) { const [price, quantity] = updateItem; // 数量为0则删除对应价格档位,兼容字符串/数字两种格式 if (quantity === '0' || Number(quantity) === 0) { // 倒序遍历删除,不会影响未遍历元素的索引 for (let i = array1.length - 1; i >= 0; i--) { // 可选链避免元素不存在时报错 if (array1[i]?.[0] === price) { array1.splice(i, 1); break; } } } else { let existed = false; for (let i = 0; i < array1.length; i++) { if (array1[i]?.[0] === price) { array1[i] = updateItem; existed = true; break; } } if (!existed) { array1.push(updateItem); } } } // 补全订单簿排序逻辑:卖盘按价格升序,买盘按价格降序 array1.sort((a, b) => { const priceA = Number(a[0]); const priceB = Number(b[0]); return type === 'asks' ? priceA - priceB : priceB - priceA; }); return array1; }
2. 修复update_orderbook状态流转逻辑
exports.update_orderbook = function(data) { if (data.e !== "depthUpdate") return; // 未就绪时先缓存深度消息,不要拼接为字符串 if (this.ready < 2) { this.depthBuffer = this.depthBuffer || []; this.depthBuffer.push(data); // 第一次收到深度消息时触发快照请求,仅触发一次 if (this.ready === 0) { this.ready = 1; console.log("Stage 1: 开始请求订单簿快照"); // 等待快照异步请求返回,完成初始化后再切换状态处理增量 this.get_orderbook_history().then(snapshot => { this.lastUpdateId = snapshot.lastUpdateId; this.asks = snapshot.asks; this.bids = snapshot.bids; this.ready = 2; console.log("Stage 2: 快照初始化完成,开始处理缓存的深度消息"); // 按顺序处理所有缓存的增量消息 this.depthBuffer.forEach(cachedData => { this.processDepthUpdate(cachedData); }); this.depthBuffer = []; }); } return; } // 就绪后直接处理新的深度消息 this.processDepthUpdate(data); } // 抽离增量更新逻辑,方便缓存消息和实时消息复用 exports.processDepthUpdate = function(data) { if (data.U <= this.lastUpdateId + 1 && data.u >= this.lastUpdateId + 1) { this.asks = sort_array(this.asks, data.a, 'asks'); this.bids = sort_array(this.bids, data.b, 'bids'); this.lastUpdateId = data.u; console.log("Stage 3: 订单簿更新完成"); } }
3. 额外注意事项
- WebSocket收到的原始消息需要先解析为JSON对象,再传入
update_orderbook,不要直接传入原始字符串 - 币安接口返回的价格、数量均为字符串格式,做数值判断时要注意类型兼容
内容的提问来源于stack exchange,提问作者user2692997
相关产品推荐
相关产品推荐

