Angular应用轮询检测新开标签页关闭状态时偶发卡死问题
问题根因
两个实现都存在定时器永久泄漏的问题,累积的轮询任务最终会占满浏览器事件循环,导致页面完全卡死:
- 所有定时器ID都定义在
foo方法的局部作用域中,后续调用foo时无法获取上一次的定时器ID,根本没有办法清理历史轮询任务 - 窗口引用存在类属性
this.someWindow上,所有未清理的旧轮询都会读取这个被覆盖的最新窗口引用,导致旧轮询永远无法触发终止条件:比如第一次打开窗口W1启动轮询T1,W1未关闭时二次调用foo打开W2,this.someWindow被覆盖为W2,T1检测到当前引用的W2未关闭就会一直运行,永远不会自动清除 - 触发概率随机的原因是,如果上一个窗口在你下次点击按钮前已经关闭,旧轮询会自动终止,就不会出现泄漏;如果上一个窗口还没关你就点了按钮,旧轮询就会永久残留,因此可能在第2次到第N次点击时触发,和你描述的现象完全匹配
- 每多残留一个轮询任务,事件循环的负担就重一分,当任务累积到一定数量,事件循环被占满,所有用户交互、页面刷新的任务都无法被调度,就会出现页面无响应、点刷新没反应的现象,只有关闭标签页销毁整个渲染进程才能恢复。
- 另外跨域外部窗口关闭后,部分浏览器访问其
closed属性会抛出跨域安全异常,未捕获的异常也会加剧应用状态异常的概率。
修复实现
按照以下逻辑调整即可解决问题,完全兼容你需要先发起HTTP请求获取URL的async场景:
- 将定时器ID、打开的窗口对象都声明为类属性,不要放在方法局部作用域
- 每次执行打开逻辑前,先清理上一次的轮询任务、重置引用
- 轮询回调增加异常捕获,处理跨域访问的特殊情况
- 组件销毁生命周期中统一清理资源,避免内存泄漏
- 优先使用
setInterval实现轮询,比递归setTimeout的清理逻辑更简单,不容易漏清理
// 类中先声明相关属性 private pollTimer: ReturnType<typeof setInterval> | null = null; private openedWindow: Window | null = null; // Angular组件销毁时清理资源 ngOnDestroy(): void { this.clearPolling(); } // 统一清理轮询的方法 private clearPolling(): void { if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = null; } this.openedWindow = null; } async foo() { // 每次打开新窗口前先清理上一轮的轮询,避免泄漏 this.clearPolling(); try { // 原有HTTP请求获取目标URL的逻辑 const targetUrl = await this.http.get('你的获取跳转链接接口').toPromise(); this.openedWindow = window.open(targetUrl, '_blank'); // 处理弹窗被浏览器拦截的情况 if (!this.openedWindow) { alert('弹窗被浏览器拦截,请允许本站弹窗后重试'); return; } } catch (err) { console.error('打开外部链接失败', err); return; } // 启动轮询 this.pollTimer = setInterval(() => { try { if (this.openedWindow?.closed) { console.log('外部窗口已关闭,执行业务逻辑'); // 此处写窗口关闭后需要执行的业务代码 this.clearPolling(); } } catch (e) { // 跨域场景下访问已关闭窗口可能抛错,按窗口关闭处理 console.log('外部窗口已关闭,执行业务逻辑'); // 此处写窗口关闭后需要执行的业务代码 this.clearPolling(); } }, 1000); }
额外优化建议
- 轮询间隔设置为1000ms即可满足绝大多数场景需求,不需要更短的间隔避免不必要的性能消耗
- 如果是Angular应用,可以将轮询逻辑包裹在
NgZone.runOutsideAngular中执行,避免每次轮询都触发Angular变更检测,业务逻辑需要更新视图时再切回NgZone.run即可,性能更好 - 因为你打开的是第三方外部站点,无法通过
postMessage做更精准的关闭通知,轮询是兼容性最高的实现方案,只要做好资源清理就不会有问题
内容的提问来源于stack exchange,提问作者mKay
相关产品推荐
相关产品推荐

