ERR_NETWORK_IO_SUSPENDED导致无限请求问题排查
问题分析
你的同步逻辑通过while(this.active)循环持续调用SomeClass.sync(),正常运行时能按需同步数据,但遇到以下场景会出问题:
- 浏览器标签页长时间非活跃或电脑休眠时,浏览器会暂停JS异步任务执行,同时挂起网络请求;
- 标签页恢复后,之前处于pending状态的
SomeClass.sync()会批量触发,加上while循环无间隔的特性,短时间内产生数千次请求,导致页面卡顿; net::ERR_NETWORK_IO_SUSPENDED是浏览器底层的网络挂起错误,部分场景下不会触发Promise的reject,因此无法被外层catch块捕获。
解决方案
1. 给同步请求添加最小间隔
在每次同步完成后增加固定的最小延迟,避免无间隔连续发起请求,即使标签页恢复后,也能有效控制请求频率:
static async sync(): Promise<void> { while (this.active) { try { await SomeClass.sync(); // 添加1秒最小间隔,可根据业务需求调整时长 await new Promise(resolve => setTimeout(resolve, 1000)); } catch (error) { console.error(error); this.active = false; } } }
2. 监听标签页可见性,暂停/恢复同步
利用Page Visibility API,在标签页不可见时暂停循环,可见时再恢复,从根源避免休眠期间积累pending请求:
static async sync(): Promise<void> { const handleVisibilityChange = () => { // 标签页不可见时,暂存活跃状态并暂停同步 if (document.hidden) { this._prevActive = this.active; this.active = false; } else { // 标签页恢复可见时,恢复活跃状态并重启同步 if (this._prevActive) { this.active = true; this.sync(); } } }; // 绑定可见性变化监听 document.addEventListener('visibilitychange', handleVisibilityChange); while (this.active) { try { await SomeClass.sync(); } catch (error) { console.error(error); this.active = false; } } // 循环结束后移除监听(可根据业务场景选择是否保留) document.removeEventListener('visibilitychange', handleVisibilityChange); } // 类内新增临时变量,用于保存标签页休眠前的活跃状态 private static _prevActive = false;
3. 结合网络状态检测
通过navigator.onLine事件监听系统网络状态,网络断开时暂停同步,恢复时再继续:
static async sync(): Promise<void> { const handleNetworkChange = () => { if (!navigator.onLine) { this._prevActive = this.active; this.active = false; } else { if (this._prevActive) { this.active = true; this.sync(); } } }; // 绑定网络状态变化监听 window.addEventListener('online', handleNetworkChange); window.addEventListener('offline', handleNetworkChange); while (this.active) { try { await SomeClass.sync(); } catch (error) { console.error(error); this.active = false; } } // 循环结束后移除监听 window.removeEventListener('online', handleNetworkChange); window.removeEventListener('offline', handleNetworkChange); } private static _prevActive = false;
4. 在SomeClass.sync()内部处理网络挂起
如果SomeClass.sync()基于fetch或XMLHttpRequest,可以手动处理网络挂起场景:
- 对于
fetch,使用AbortController在标签页不可见时主动中止请求,避免pending状态积累:
// 修改SomeClass.sync()方法 static async sync(): Promise<void> { const controller = new AbortController(); const signal = controller.signal; // 监听标签页可见性,不可见时中止请求 const abortOnHidden = () => { if (document.hidden) controller.abort(); }; document.addEventListener('visibilitychange', abortOnHidden); try { const response = await fetch('/your-sync-api', { signal }); // 处理同步响应逻辑 } catch (error) { if (error.name === 'AbortError') { // 捕获主动中止的错误,无需向外抛出 return; } throw error; } finally { document.removeEventListener('visibilitychange', abortOnHidden); } }
内容的提问来源于stack exchange,提问作者Mdka
相关产品推荐
相关产品推荐

