onclose事件不可靠,需结合心跳超时与send异常捕获来及时发现断连;readyState===CLOSED仅表示收到关闭信号,不能确认当前是否断开;应通过safeSend防护、主动心跳+超时关闭、beforeunload/pagehide清理等组合策略保障连接状态准确感知。

不能只靠 onclose 事件判断连接已断开——它可能根本不会触发,或者延迟数分钟才来。
为什么 onclose 不可靠
WebSocket 的 onclose 是被动回调,依赖服务端发关闭帧或 TCP 层最终断连通知。但现实中常见以下情况:
- 中间代理(如 Nginx、CDN)静默 kill 空闲连接,不发关闭帧 →
onclose不触发,readyState长期卡在OPEN - 手机锁屏/切后台后网络被系统休眠,心跳收不到响应,但浏览器仍认为连接“活着”
- 服务端进程崩溃或 OOM,连接直接断开,部分环境
onclose被丢弃或延迟送达
此时你调用 socket.send() 会立即抛 InvalidStateError,但没人监听这个异常。
readyState === WebSocket.CLOSED 什么时候才准
readyState 是当前最易获取的状态值,但它只有三种稳定状态:CONNECTING(0)、OPEN(1)、CLOSING(2)、CLOSED(3)。注意:
-
CLOSED(3)仅在close()被显式调用,或onclose完整执行后才会设为该值 - 连接被强制中断时,
readyState可能长期停留在OPEN(1),直到下一次 send 或底层探测失败 - 所以检查
readyState === WebSocket.CLOSED只能确认“已收到关闭信号”,不能确认“当前是否断开”
更实用的做法是组合判断:socket.readyState !== WebSocket.OPEN || socket.readyState === WebSocket.CLOSED,再配合心跳超时做最终裁定。
HISUI 前端框架基础开发指南。HISUI 是基于 easyui 二次开发的 jQuery UI 组件库,专为医疗信息系统设计。当涉及 HISUI 组件开发、jQuery easyui 表格/表单/布局/窗口、医疗系统前端页面编写时使用此 skill。 涵盖 40 个组件的官方文档参考,支持声明式与编程式两种初始化方式,提供 $cm/$m 数据访问方法。
真正能及时发现断开的是心跳超时 + send() 异常捕获
这是目前前端唯一能主动、低延迟感知断连的方式。核心逻辑不是“等断开”,而是“证活着”:
- 每 30s 发一次
socket.send(JSON.stringify({ type: "ping", ts: Date.now() })) - 同时启动一个 15s 的
serverTimeoutTimer,只要收到任意onmessage(pong 或业务消息)就clearTimeout - 若超时,立刻执行:
socket.close(4999, "heartbeat timeout"),然后触发提示和重连 - 在发送关键消息前,加一层保护:
function safeSend(data) { if (socket.readyState !== WebSocket.OPEN) { throw new Error("Socket not ready"); } try { socket.send(data); } catch (e) { if (e.name === "InvalidStateError") { // 此刻连接已实际断开,但 readyState 还没更新 handleDisconnect(); } } }
onbeforeunload 关闭前必须调用 close()
用户刷新或关标签页时,不主动关闭会导致服务端残留连接,还可能触发 "Connection closed without close frame" 警告:
- 务必在
window.addEventListener('beforeunload', ...)中判断socket.readyState === WebSocket.OPEN再调用socket.close(1000, "page unload") - 不要在
onclose回调里再调close()—— 会报错且无意义 - 移动端需额外监听
pagehide和visibilitychange,因为beforeunload在 iOS Safari 中不可靠
真正难的不是写监听,而是把心跳、异常捕获、状态清理、重连退避全串成一条不漏的链路——任何一环松动,都会让“已断开”变成“疑似断开”,拖慢故障反馈速度。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










