只有 ws.readystate === websocket.open 才算真正连接成功,其他值都不代表可通信;需在 onopen 后发握手消息并等待服务端 ack 才能发业务消息;重连须确保旧实例已清除且仅在 onclose/onerror 中触发;可靠判断需结合事件、心跳与服务端 ack。
只有 ws.readystate === websocket.open 才算真正连接成功,其他值都不代表可通信——这是最常被忽略的硬性前提。
为什么不能用 ws.readyState === 1 直接判断
虽然 WebSocket.OPEN 的数值确实是 1,但直接写 === 1 会埋下兼容性和可维护隐患:
- 部分旧环境(如 Android 4.4 WebView)不支持
WebSocket.CONNECTING等常量,但更关键的是——它不支持你未来重构时快速定位状态含义 -
== 1在老版 Safari 中曾因类型转换导致误判(比如某次 error 后 readyState 变成字符串 "1") - 数字字面量无法自我说明意图,
WebSocket.OPEN一眼可知语义,也方便 TypeScript 类型推导
onopen 触发时 readyState 一定是 1,但不代表能立刻 send()
浏览器触发 onopen 表示协议握手完成,readyState 已切换为 WebSocket.OPEN,但这只是“通道就绪”,不是“业务就绪”:
- 服务端可能还在做鉴权、会话初始化等异步操作,此时发业务消息会被静默丢弃
- Nginx 或 CDN 可能缓存了 101 响应,但后续帧传输被拦截,客户端无感知
- 极低概率下,浏览器内部 TCP 状态与 JS 层 readyState 不完全同步(尤其低端设备)
建议在 onopen 中先发一个轻量 auth 或 handshake_ack 消息,并等待服务端明确返回 { type: "ack", ok: true } 后,再放开业务消息队列。
重连逻辑里最容易踩的 readyState 坑
自动重连不是看到 readyState !== 1 就 new WebSocket(),那样会引发竞态和内存泄漏:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 在
WebSocket.CLOSING(2)状态下新建连接,旧实例尚未释放,监听器残留,导致重复响应 - 未清空定时器就重试,
onclose触发多次后,多个重连定时器叠加执行 - 没检查
ws.readyState === WebSocket.CLOSED(即 3),可能对正在关闭中的实例重复 close()
安全做法是:只在 onclose 或 onerror 回调中启动重连;重连前设 ws = null;新实例创建前确保旧引用已清除。
调试时别信控制台里随手打的 console.log(ws.readyState)
你在 DevTools 里敲下这行代码,看到输出 1,只代表那一毫秒的状态快照。它无法反映:
- 是否刚收到
onclose但事件循环还没执行回调 - 是否正处在
CLOSING → CLOSED的不可预测过渡期(此时send()必然抛InvalidStateError) - 网络已断开但浏览器尚未降级
readyState(OPEN 状态可能维持数秒)
真正可靠的判断必须结合事件(onopen/onclose/onerror)+ 主动心跳 + 服务端 ACK,三者缺一不可。










