Web Worker 实现长轮询需封装完整生命周期:用 fetch+AbortController 控制请求、超时与中断,异常走指数退避,通信带 ID 且限 JSON 子集,终止前主动 abort 并单例管理,兼容性检测 fallback 到 XMLHttpRequest。

用 Web Worker 做长轮询,核心是把耗时等待从主线程搬走,让 UI 不卡、不假死。关键不在“能不能轮”,而在于“怎么稳住连接、怎么安全通信、怎么干净收尾”——尤其内存泄漏,往往出在忘记终止或重复创建上。
长轮询逻辑必须封装在 Worker 内部
主线程不该碰 fetch、超时控制、重试循环这些。Worker 脚本要自主管理整个生命周期:
- 用 fetch + AbortController 发起请求,设好超时(比如 25–30 秒),避免挂起太久
- 响应一返回(无论成功或空数据),立刻发起下一轮,保持“请求-响应-再请求”闭环
- 捕获常见异常:网络中断、503/401 状态码、JSON 解析失败,统一走指数退避重试(如 1s → 2s → 4s)
- 不依赖全局变量或闭包状态,所有上下文通过
self和 message 传递,保证可销毁性
双向通信要带标识、讲结构、避大对象
postMessage 不是万能管道,乱传会卡顿甚至序列化失败:
- 主线程发指令时带上 唯一 request ID 或 type 字段,比如
{ type: 'start', id: 'poll-123' } - Worker 返回数据也带对应
id或type,主线程靠这个匹配和分发,避免消息错位 - 只传 JSON 安全子集:普通对象、数组、字符串、数字、布尔值;禁传函数、Date、RegExp、Blob(需先转成 ArrayBuffer 或 base64)
- 高频小数据(如心跳、状态)可用 BroadcastChannel 补充,它支持多标签页+Worker 同时监听同名频道,比反复 postMessage 更轻量
终止 Worker 必须主动、及时、可预测
worker.terminate() 是唯一真正释放资源的方式,但时机不对反而引发问题:
- 页面卸载前,在
beforeunload或visibilitychange中通知 Worker 停止当前 fetch,并退出轮询循环 - 收到 stop 指令后,Worker 内部应调用
abortController.abort()中断 fetch,再 return 出循环,而不是等下次超时 - 主线程随后调用
worker.terminate()—— 注意:这会立即杀死线程,不等 onmessage 回调,所以清理动作必须在 Worker 内部完成 - 避免每次组件 mount 都 new Worker;建议封装成单例,或由全局状态(如 Redux store 或 Context)统一控制启停
兼容与降级要写在 Worker 初始化阶段
不是所有环境都支持 fetch 或 AbortController,尤其旧版浏览器或某些 WebView:
- Worker 脚本开头做能力检测:
if (!self.AbortController || !self.fetch),不满足就 postMessage 报错并退出 - 可 fallback 到 XMLHttpRequest,但注意 Worker 中不支持 onreadystatechange,只能靠 timeout + onload/onerror 组合判断
- 主线程监听到该错误后,可切换为惰性 setTimeout 轮询(即上一次请求结束才设下一次),虽不如长轮询实时,但至少不阻塞 UI

















