工程上最危险的不是完全不可用,而是在理想环境里正常、进入真实网络和真实数据量后悄悄失去边界。 后台标签可能停止定时器和 JavaScript,却不一定立即关闭 WebSocket 或 PeerConnection。应用不能把一次心跳延迟当离线,也不能假设醒来后的旧连接仍然有效。
浏览器能力由版本、权限、页面可见性和设备策略共同决定。实现需要能力探测与可解释降级,不能把某一台开发机上的 API 存在当成产品契约。
工程边界与取舍
实现之前先列出不可违反的不变量,再为性能与体验留出调节参数。参数可以灰度,身份、权限和终态规则不能在运行时随意漂移。
- 把可见性、执行冻结、网络在线和远端会话拆成独立状态,不压成一个 online。
- 进入 freeze 前持久化未完成任务与用户停止意图,禁止在事件里做长异步工作。
- resume 后通过会话版本、心跳和 selected candidate pair 重新对账,再恢复功能。
实时网页要把“代码暂停执行”视为常态。恢复不是继续上一次回调,而是从持久状态和远端事实重新确认当前所有权。
生产环境会怎样出错
失败路径需要与成功路径共享同一套状态模型。只弹一个错误提示却不释放资源、不传播终态,下一次恢复就会继承脏状态。
- 后台节流让重连计时器集中醒来,数百标签同时冲击信令服务。
- pagehide 被当永久退出,进入 bfcache 的页面错误撤销信任和任务。
- 恢复后沿用旧文件发送循环,新循环又启动,导致重复分块和进度倒退。
把测试跑成闭环
测试不能以“最后成功了”结束。还要测量经历了多少次副作用、等待多久、是否泄露内容,以及失败后再次执行能否从干净基线开始。
- 让页面后台五分钟、系统休眠和内存冻结分别恢复,检查任务只存在一份。
- 用被节流的假时钟批量触发超时,验证重试带抖动且有全局并发上限。
- 从 bfcache 返回时确认页面不重复注册事件、WebSocket 或分析监听器。
当实现能够安全降级、重复执行不产生额外副作用、故障指标早于投诉出现时,这项能力才算具备长期维护价值。