定时器休眠事件及解决方案

前言

为什么会出现这个呢,因为之前产品搞了一个倒计时,需要倒计时结束之后重新请求接口。但是在实际使用的时候发现一个问题----用户隐藏tab之后重新打开,会导致倒计时不准从而导致请求接口的时机出现延误。经过调研发现是定时器休眠导致的。所以进行了一次总结。
这里是之前的定时器代码 ---- 手把手带你撸一个精准倒计时。

遇到的问题

  1. 现在的浏览器为了极致的性能优化,对于定时器等功能都有一定的魔改。比如:当我们在倒计时运行期间因为某些事情隐藏浏览器或者切换tab的时候,浏览器会将定时器的执行周期延长到一分钟。这会导致倒计时功能的不精准。
Chrome 88后更新了更为激进的省电策略[[1]](https://www.zhihu.com/question/263914822#ref_1),导致后台非活动页面中setInterval、setTimeout回调的执行间隔拉长到1分钟以上。

解决方案: 如果是电脑端浏览器的话,可以监听页面的隐藏和显示,在判断用户重新打开浏览器的时候,进行接口调用,重置定时器事件。如果是定时器这类的推荐这种使用方法,可以保证定时器的准确性

var hidden, visibilityChange;
if (typeof document.hidden !== "undefined") {
    hidden = "hidden";
    visibilityChange = "visibilitychange";
} else if (typeof document.mozHidden !== "undefined") {
    hidden = "mozHidden";
    visibilityChange = "mozvisibilitychange";
} else if (typeof document.msHidden !== "undefined") {
    hidden = "msHidden";
    visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
    hidden = "webkitHidden";
    visibilityChange = "webkitvisibilitychange";
}
// 添加监听器
document.addEventListener(visibilityChange, function() {
    if(!document[hidden]){
      console.log('在这里进行定时器事件的重置');
    }
}, false);
  1. 在页面隐藏期间,我们所造成倒计时效果处于等待阶段,但是事件依然在生成堆积,这就会导致一次大爆发。当我们重新显示浏览器的时候,积攒的时间会一次性的爆发出来。这里最直观的体现在于弹幕系统或者时钟系统的动画。这些动画事件会在显示的时候进行大量更新。从而导致页面效果与预期的不符。

解决方案:
针对这种堆积事件,我们可以使用 setTimeout递归模拟setInterval 来解决,堆积的机制是因为setInterval在休眠的时候依然在不停的添加时间。如果我们使用setTimeout去模拟的话,就会避免这种问题,不会进行堆积。这里不同的浏览器的反应不同,大体有两种情况:第一种:隐藏tab也会继续执行setTimeout的递归第二种: 隐藏tab之后会暂停事件执行和插入,等重新显示的时候继续执行。

// setTimeout递归
function getDate() {
	getTimeOptions();
	setTimeout(() => {
		getDate();
	}, 1000);
}
getDate();

其他的解决方案

使用播放视频的时候浏览器不休眠的机制 将以下代码添加到页面之中执行即可。不过对于Safari,该方法无效。

const chromeVersion = /Chrome\/([0-9.]+)/
  .exec(window?.navigator?.userAgent)?.[1]
  ?.split('.')[0];

if (chromeVersion && parseInt(chromeVersion, 10) >= 88 && !isMobile()) {
  const videoDom = document.createElement('video');
  const hiddenCanvas = document.createElement('canvas');

  videoDom.setAttribute('style', 'display:none');
  videoDom.setAttribute('muted', '');
  videoDom.muted = true;
  videoDom.setAttribute('autoplay', '');
  videoDom.autoplay = true;
  videoDom.setAttribute('playsinline', '');
  hiddenCanvas.setAttribute('style', 'display:none');
  hiddenCanvas.setAttribute('width', '1');
  hiddenCanvas.setAttribute('height', '1');
  hiddenCanvas.getContext('2d')?.fillRect(0, 0, 1, 1);
  videoDom.srcObject = hiddenCanvas?.captureStream();
}

使用webWoker
Web Workers 是 HTML5 提供的一个javascript多线程解决方案,可以将一些大计算量的代码交由web Worker运行而不冻结用户界面。 注意:Web Workers需要IE10及其以上的浏览器支持。

参考文献

  1. www.zhihu.com/question/26…

扩列

vx.jpg