必须在进入 frozen 状态前通过 pagehide 或 visibilitychange 事件清理连接,因为 frozen 状态下 javascript 完全停摆、无法执行任何代码;推荐监听 pagehide(判断 persisted)和 visibilitychange(检查 hidden),配合 navigator.locks 防竞态,并通知服务端优雅降级。

页面进入 frozen 状态时,浏览器已暂停其 JavaScript 执行、定时器、网络请求和事件响应,此时无法主动执行代码来“挂起连接”。因此,不能在 frozen 状态中触发挂起逻辑——必须在进入 frozen 之前(即 visibilitychange 或 pagehide 事件中)完成连接清理。
理解 frozen 状态的不可操作性
frozen 是 Page Lifecycle API 定义的静默状态,本质是浏览器对页面的深度冻结:JS 引擎停摆、Event Loop 暂停、所有异步任务(包括 setTimeout、fetch、WebSocket 回调)均被挂起。你无法监听或响应 frozen 本身,也无法在该状态下运行任何代码。
关键事实:
-
document.onfreeze仅是一个通知钩子,触发时页面已冻结,无法执行副作用 -
freeze事件不可取消,也不支持异步延迟 - 所有连接(WebSocket、EventSource、长轮询、未完成 fetch)会由浏览器自动中断或置于“待恢复”状态,但行为不一致且不可控
在 pagehide + visibilitychange 中提前清理连接
真正可行的时机是页面即将失活但尚未冻结前——pagehide(兼容性好,含 bfcache 场景)和 visibilitychange(标签页切走时触发)。此时 JS 仍可执行,是最后的安全窗口。
推荐做法:
- 监听
pagehide,判断event.persisted === false(非 bfcache)时主动关闭连接;若为true,则保留连接并依赖 bfcache 恢复机制 - 同时监听
visibilitychange,当document.visibilityState === 'hidden'且页面未卸载时,关闭非必要长连接(如心跳、日志上报) - 对 WebSocket 使用
close(1001, "Going away");对 EventSource 调用eventSource.close();对活跃 fetch 使用AbortController提前中止
结合 navigator.locks 防止重复挂起与竞态
多标签页或 iframe 场景下,多个实例可能同时响应 pagehide。可用 navigator.locks.request() 确保挂起逻辑只执行一次:
- 在
pagehide中申请名为"cleanup-connections"的锁 - 获得锁后检查全局标记(如
window.__connections_cleaned),避免重复关闭 - 执行关闭逻辑后释放锁,不影响其他标签页后续操作
服务端配合:识别客户端静默并优雅降级
前端挂起连接只是半程。服务端需配合处理:
- WebSocket 连接关闭时发送明确 reason(如 1001),服务端据此清理 session 或切换为离线模式
- 对 HTTP 长轮询,前端在 pagehide 中主动发
/api/leave?reason=hidden通知服务端终止等待 - 服务端设置合理的连接空闲超时(如 30 秒),兜底处理前端未及时通知的场景
不复杂但容易忽略:frozen 不是执行点,而是结果。所有挂起动作必须前置到 pagehide/visibilitychange,再辅以锁机制和服务端协同,才能实现真正可靠的静默期连接管理。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










