Socket.io中Promise.all返回结果异常问题求助
问题
Socket.io服务端中,单个socket.emit()能获取预期结果,但Promise.all处理后输出异常。以下是代码及日志:
服务端代码
socket.on('hh:broadcast', (props, cb) => { const session = sockets.get(socket); if (!session) return; const [room, that] = session; const collaborators = sessions.get(room)! Promise.all<ArrayElement<NonNullable<Parameters<typeof cb>['0']>>>(collaborators .filter(([, other]) => other !== that) .map(([sock, other]) => sock .emitWithAck('hh:data', { id: that, ...props }) .then(res => { log.info("received", other, res); return { id: other, ...res }; })) ).then(res => { log.warn("Promise.all:", JSON.stringify(res)) cb(res); }); });
日志信息
单个Promise回调能收到完整Buffer数据:
19:43:28.431 [WebSocket] received gDmNz9Ih2WWw {data: {data: [49, 237, 191, 23, 68, 120, 57, 163, 65, 57, 62, 200, 19, 155, 179, 23, 183, 242, 236, 97, 164, 99], type: "Buffer"}} 19:43:28.431 [WebSocket] received mX6SfA2F0MsD {data: {data: [49, 229, 183, 22, 72, 120, 202, 117, 249, 93, 17, 39, 226, 13, 101, 160, 208, 243, 76, 82, 95, 116], type: "Buffer"}}
但Promise.all返回结果中部分客户端data为空数组:
19:43:28.431 [WARNING WebSocket] Promise.all: [{"id":"mX6SfA2F0MsD","data":{"type":"Buffer","data":[49,229,183,22,72,120,202,117,249,93,17,39,226,13,101,160,208,243,76,82,95,116]}},{"id":"gDmNz9Ih2WWw","data":{"type":"Buffer","data":[]}}]
明明每个Promise都拿到正确结果,为什么Promise.all合并后的数组会有异常?
分析与解决
问题出在Socket.io的Buffer序列化/反序列化逻辑上:
- 单个Promise的
log.info打印时,客户端返回的Buffer还没被Socket.io的JSON序列化流程处理,此时res.data是完整的Buffer实例; - 但等到
Promise.all收集完结果执行JSON.stringify时,Socket.io可能复用或修改了原Buffer实例的内容,导致最终输出的数组为空。
具体修复方案
在每个Promise的then回调里,把Buffer数据深拷贝成普通数组,切断对原Buffer实例的引用,避免后续操作篡改数据:
.then(res => { log.info("received", other, res); // 拷贝Buffer的data数组,确保数据不被后续修改 const safeRes = { ...res, data: res.data ? { type: res.data.type, data: [...res.data.data] // 深拷贝数组内容 } : res.data }; return { id: other, ...safeRes }; })
也可以直接把Buffer转成Base64字符串传递,彻底避开Socket.io的二进制序列化问题:
.then(res => { log.info("received", other, res); const safeRes = { ...res, data: res.data ? res.data.toString('base64') : res.data }; return { id: other, ...safeRes }; })
核心原因
Socket.io处理二进制数据时,会复用Buffer实例或在序列化阶段修改其内容。单个Promise的日志打印在修改前,所以能看到完整数据;而Promise.all的结果在修改后序列化,就出现了空数组。通过深拷贝或格式转换,就能保证数据独立不受后续操作影响。
内容的提问来源于stack exchange,提问作者Fabi
相关产品推荐
相关产品推荐

