如何避免Socket新连接时同一ID重复触发setInterval调用API?
解决方案
核心思路是维护一个全局的匹配状态管理对象,记录每个match_id对应的定时器和订阅该匹配的Socket连接,以此避免重复发起API请求,同时统一推送数据给所有订阅用户。
修改后的完整代码
// 全局对象:存储活跃匹配的定时器和订阅Socket集合 const activeMatches = {}; socket.on('fancy', async (data) => { const { match_id } = data; // 检查该匹配是否已有运行中的定时任务 if (activeMatches[match_id]) { // 将当前Socket加入订阅列表 activeMatches[match_id].sockets.add(socket); // 可选:立即推送一次当前API数据给新订阅用户,不用等下一秒 try { const fancy = await callapi(match_id); socket.emit(`fancy_${match_id}`, fancy); } catch (err) { console.error(`获取匹配${match_id}数据失败:`, err); } return; } // 初始化该匹配的订阅Socket集合(用Set避免重复添加同一Socket) const sockets = new Set(); sockets.add(socket); // 创建定时任务,每秒调用API并推送给所有订阅用户 const intervalId = setInterval(async () => { try { const fancy = await callapi(match_id); // 遍历所有订阅Socket推送数据 sockets.forEach(sock => { sock.emit(`fancy_${match_id}`, fancy); }); } catch (err) { console.error(`定时获取匹配${match_id}数据失败:`, err); // 可选:如果API持续失败,可考虑停止定时任务并清理状态 // clearInterval(intervalId); // delete activeMatches[match_id]; } }, 1000); // 将该匹配的状态存入全局对象 activeMatches[match_id] = { intervalId, sockets }; // 监听Socket断开事件,清理订阅关系 socket.on('disconnect', () => { sockets.delete(socket); // 如果该匹配没有订阅用户了,停止定时任务并释放资源 if (sockets.size === 0) { clearInterval(intervalId); delete activeMatches[match_id]; console.log(`匹配${match_id}已无订阅用户,停止定时任务`); } }); }); // API调用函数(优化变量声明和错误处理) const callapi = (id) => { return new Promise((resolve, reject) => { const options = { method: 'POST', url: 'url', formData: { id } }; request(options, function (error, response) { if (error) { console.error(`API请求失败(ID: ${id}):`, error); return reject(error); } try { const data = JSON.parse(response.body).data; resolve({ status: 'success', data }); } catch (parseErr) { console.error(`API响应解析失败(ID: ${id}):`, parseErr); reject(parseErr); } }); }); };
关键改进说明
- 避免重复任务:通过
activeMatches全局对象跟踪每个match_id的运行状态,同一匹配只会启动一个定时任务。 - 统一数据推送:每个匹配的API响应会推送给所有订阅用户,不用为每个用户单独调用API,减少服务器压力。
- 自动资源清理:用户断开连接时自动从订阅列表移除;当某匹配没有订阅用户时,立即停止定时任务并清理内存,防止无效资源占用。
- 优化错误处理:给API调用和响应解析环节添加错误日志,便于排查问题,也可根据业务需求添加重试逻辑。
- 提升用户体验:新用户订阅时立即推送一次当前数据,不用等待下一秒的定时任务。
内容的提问来源于stack exchange,提问作者Piyush
相关产品推荐
相关产品推荐

