WebSocket重连次数限制需用闭包或类属性维护计数器,否则每次新建实例导致retryCount重置失效;必须在onopen后重置计数器,重连前清除旧定时器、校验readyState、检查document.hidden和navigator.onLine以避免无效请求。

WebSocket重连次数限制必须用闭包或类属性维护计数器
直接在 onclose 里写 let retryCount = 0 是无效的——每次重连都新建 WebSocket 实例,变量被重置,限制就失效了。必须把 retryCount、reconnectTimer 等状态提到外层作用域,或封装进类/工厂函数。
常见错误现象:retryCount 始终是 1,重连无限进行;或者多个 ws 实例共用一个计数器,导致本该重连的连接被跳过。
- 用
let retryCount = 0+let reconnectTimer = null声明在 connect 函数外部(模块级) - 若需支持多实例(如多个不同地址的 ws),改用工厂函数返回带独立状态的对象
- 每次成功
onopen后必须重置retryCount = 0,否则下次断开直接触发上限判断
递归重连逻辑不能裸写 setTimeout(connect, delay)
递归调用 connect() 自身容易失控:没清旧定时器、没校验当前连接状态、没防重复调用,轻则内存泄漏,重则页面卡死。
关键动作必须在每次重连前执行:
- 先
clearTimeout(reconnectTimer),避免上一轮未触发的定时器在新连接建立后误执行 - 检查
ws?.readyState === WebSocket.CLOSED || ws?.readyState === WebSocket.CONNECTING,防止重复 new WebSocket - 用
if (retryCount 包裹整个重连分支,达到上限后彻底退出,不递归也不设新定时器
示例片段(精简可运行):
let ws = null;
let retryCount = 0;
const MAX_RETRY = 5;
let reconnectTimer = null;
<p>function connect() {
ws = new WebSocket('ws://localhost:8000/ws');</p><p>ws.onopen = () => {
console.log('connected');
retryCount = 0; // ✅ 成功后清零
};</p><p>ws.onclose = (event) => {
if (retryCount >= MAX_RETRY) {
console.error('max retry reached, stop auto-reconnect');
return;
}
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000);
retryCount++;
clearTimeout(reconnectTimer); // ✅ 清除上一次
reconnectTimer = setTimeout(connect, delay);
};
}
onclose 里不判断 event.code 就重连,会误判正常关闭
event.code === 1000 表示服务端主动发起的优雅关闭(比如发布新版本时通知客户端下线),此时不该重连,否则用户刚退出就被拉回,体验极差。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
真正需要重连的典型 code 是:
-
1006:异常关闭(无 close 帧),基本可认定为网络中断或进程崩溃 -
4000–4999范围内的自定义业务码,比如4001(token 过期)、4002(权限变更),这类需配合刷新凭证再重连,而非无脑重试
所以 onclose 里应优先判断:
ws.onclose = (event) => {
if (event.code === 1000) {
console.log('graceful shutdown, skip reconnect');
return;
}
if (event.code === 1006 || event.code >= 4000) {
// 启动重连逻辑
}
};
重连前不检查 document.hidden,锁屏/切后台还在狂发请求
手机锁屏、浏览器标签页切换到后台时,setTimeout 仍会执行,但 WebSocket 握手大概率失败,还白白耗电、占连接数。
简单加一层判断即可规避:
if (document.hidden) { setTimeout(() => { /* 再检查一次 hidden */ }, 3000); return; }- 更稳妥的做法是监听
visibilitychange事件,页面回到前台时才恢复重连尝试 - 搭配
navigator.onLine可进一步过滤完全离线状态(但注意:它只反映系统级联网,不保证能连到你的 ws 地址)
这个细节在中后台项目里特别重要——管理员开着监控页去开会,回来发现页面报了 20 次重连失败,实际只是他合上了笔记本。

















