如何在包含事件监听器的JavaScript函数中返回回调数据?
基于WebSocket的带唯一ID匹配的请求封装实现
你当前的写法存在两个核心问题:
async函数没有关联Promise实例,无法获取onmessage异步回调里的结果直接返回- 每次调用请求函数都重新给
ws.onmessage赋值,会覆盖之前绑定的事件逻辑,旧的请求永远无法收到对应响应
核心实现思路
利用Promise挂起请求,全局维护请求ID与对应resolve回调的映射表,收到服务端响应后通过ID匹配到对应的Promise并返回结果,同时补充超时逻辑避免内存泄漏。
完整实现代码
1. 全局基础配置(仅需初始化一次)
// WebSocket实例,这里假设你已经完成了ws的连接初始化 const ws = new WebSocket('ws://你的服务端地址') // 全局请求映射表:key为请求ID,value为对应Promise的resolve回调和超时定时器ID const requestMap = new Map() // 生成唯一请求ID的工具方法,业务复杂度高可替换为uuid生成逻辑 const generateReqId = () => `${Date.now()}-${Math.random().toString(36).slice(2, 8)}` // 全局仅绑定一次onmessage事件处理逻辑 ws.onmessage = (event) => { // 前后端约定响应报文为JSON格式,必须携带前端传入的reqId字段 const response = JSON.parse(event.data) const { reqId, ...responseData } = response // 匹配到对应请求 if (reqId && requestMap.has(reqId)) { const { resolve, timeoutId } = requestMap.get(reqId) // 清除超时定时器 clearTimeout(timeoutId) // 返回响应结果 resolve(responseData) // 移除映射记录,避免内存泄漏 requestMap.delete(reqId) } } // 可选:WebSocket断开时清空所有 pending 状态的请求,避免一直等待 ws.onclose = () => { for (const [reqId, { timeoutId, reject }] of requestMap.entries()) { clearTimeout(timeoutId) reject(new Error('WebSocket连接已断开')) requestMap.delete(reqId) } }
2. 请求函数封装
/** * WebSocket请求封装 * @param {Object} params 请求业务参数 * @param {Number} timeout 超时时间,默认10秒 * @returns {Promise} 服务端响应结果 */ async function websocketRequest(params, timeout = 10000) { return new Promise((resolve, reject) => { const reqId = generateReqId() // 注册超时逻辑 const timeoutId = setTimeout(() => { requestMap.delete(reqId) reject(new Error('请求超时')) }, timeout) // 存储请求回调和定时器到映射表 requestMap.set(reqId, { resolve, reject, timeoutId }) // 发送请求,携带唯一reqId ws.send(JSON.stringify({ reqId, ...params })) }) }
3. 调用示例
// 业务代码调用 async function fetchUserInfo() { try { const result = await websocketRequest({ action: 'getUserInfo', userId: 10086 }) console.log('用户信息:', result) } catch (err) { console.error('请求失败:', err.message) } }
注意事项
- 需要和后端约定好报文格式,服务端响应必须原封不动返回前端传入的
reqId字段,才能完成请求与响应的匹配 - 不要重复绑定
ws.onmessage事件,仅初始化时绑定一次即可 - 生产环境建议添加断连重连逻辑,重连后可根据业务需要判断是否要重试未完成的请求
内容的提问来源于stack exchange,提问作者svoruganti
相关产品推荐
相关产品推荐

