WebWorker运行一段时间致Chrome标签页崩溃问题排查与解决
问题分析与修复方案
问题现象
在React组件中使用WebWorker每隔1秒调用API获取数据,初期运行正常,一段时间后Chrome标签页冻结无响应,WebWorker仍在后台运行,且CPU占用率极高。
原因分析
- WebWorker未被正确销毁:React组件的
useEffect清理函数仅设置加载状态,未调用worker.terminate()。组件卸载后WebWorker会持续在后台运行,若组件多次挂载(如路由切换),会创建多个独立的WebWorker实例,每个都在每秒发起请求,CPU占用持续累积。 - 定时逻辑导致请求堆积:当前用递归
setTimeout实现的定时任务,调用getData后立刻设置下一个1秒定时器,完全不等待API请求完成。若API响应耗时超过1秒,会导致多个请求并发执行,随着时间推移并发请求数量持续增加,加剧CPU和网络负载。 - 错误处理不完整:
try/catch无法捕获fetch的HTTP错误(如4xx/5xx状态码),这类错误会被忽略,可能导致无意义的重复请求,进一步消耗资源。
修复方案
1. 组件卸载时终止WebWorker
在useEffect的清理函数中添加worker.terminate(),确保组件销毁时同步终止WebWorker,避免后台残留实例:
useEffect(() => { setIsLoading(true) const worker = new Worker('worker.js') const token = localStorage.getItem(Constants.TOKEN); const backendServer = window['backendServer']; worker.postMessage({ _subservice, token, backendServer }) worker.onmessage = (event) => { setData(event.data) setIsLoading(false) } worker.onmessageerror = (event) => { console.log('msg errrrrrror', event) } worker.onerror = (event) => { console.error('error received from worker:', event); } return () => { setIsLoading(false) worker.terminate() // 销毁WebWorker }; }, []);
2. 修改WebWorker定时逻辑,等待请求完成后再启动下一次任务
将递归定时器改为在API请求完成(成功/失败)后再设置下一次定时,避免请求堆积:
onmessage = (event) => { const { _subservice, token, backendServer } = event.data const getData = async () => { const body = { subservice: _subservice, command: 'info' }; try { const response = await fetch(backendServer, { method: 'POST', headers: { 'Authorization': `Token ${token}`, 'Content-Type': 'application/json' // 指定请求格式,避免服务器解析异常 }, body: JSON.stringify({ 'data': body }) }); // 处理HTTP错误状态码 if (!response.ok) { throw new Error(`请求失败:${response.status}`); } const data = await response.json(); postMessage(data.response); } catch (error) { console.error('获取数据失败:', error); } finally { // 请求完成后,1秒后发起下一次请求 setTimeout(nextInterval, 1000); } }; const nextInterval = () => { getData(); } // 启动首次请求 nextInterval(); }
3. 防止重复启动定时循环(可选)
若担心WebWorker收到多次message导致重复启动定时任务,可添加运行状态标志:
let isTaskRunning = false; onmessage = (event) => { if (isTaskRunning) return; // 已有任务在运行,直接返回 isTaskRunning = true; const { _subservice, token, backendServer } = event.data const getData = async () => { // 同上逻辑... }; const nextInterval = () => { getData(); } nextInterval(); }
内容的提问来源于stack exchange,提问作者Fateme
相关产品推荐
相关产品推荐

