send()在连接成功前调用会同步抛出InvalidStateError,无法被onerror捕获,必须前置检查readyState或封装等待逻辑。

在连接成功前调用 send() 会直接抛出 InvalidStateError,这不是可捕获的“异常事件”,而是同步运行时错误——它不会触发 onerror,也不会被 try...catch 捕获(除非你显式把 send() 包在 try 块里)。
为什么 onerror 不会触发
ws.onerror 是异步事件处理器,只响应底层网络或协议层的意外错误(比如 DNS 失败、TLS 握手失败、连接被重置),不处理状态校验失败。而 send() 在 readyState !== OPEN 时被浏览器主动拦截并抛错,属于规范强制行为,不属于“连接异常”范畴。
真正有效的捕获方式
必须前置防御,不能依赖事后捕获:
-
每次 send 前检查 readyState:最简单可靠,适合多数场景
if (ws.readyState === WebSocket.OPEN) { ws.send(msg); } -
封装带状态等待的发送函数:用 Promise 等待
onopen完成,再执行发送
例如:await waitForOpen(ws); ws.send(msg); -
在 try 中手动包裹 send 调用:仅作兜底,不推荐作为主逻辑
try { ws.send(msg); } catch (e) { if (e.name === 'InvalidStateError') { console.warn('连接未就绪,消息丢弃'); } }
常见误操作与后果
这些做法无法真正解决问题:
- 只监听
onerror并期望它捕获send报错 —— 实际不会触发 - 在 Vue
mounted或 ReactuseEffect初始化时立刻send—— 此时readyState很可能还是0(CONNECTING) - 创建 WebSocket 实例后不立即绑定
onopen,延迟绑定可能错过事件 ——onopen只触发一次
推荐初始化写法(防错+可读)
确保从创建那一刻起就具备状态感知能力:
const ws = new WebSocket('wss://example.com');
// 立即绑定,不遗漏
ws.onopen = () => console.log('已就绪,可安全发送');
ws.onerror = e => console.error('底层连接异常', e);
ws.onclose = e => console.log('连接关闭', e.code, e.reason);
// 发送前必查
function safeSend(msg) {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(msg));
} else if (ws.readyState === WebSocket.CONNECTING) {
console.warn('连接中,暂不发送');
} else {
console.warn('连接不可用,状态:', ws.readyState);
}
}


















