瀏覽器切換到其他標(biāo)簽頁(yè)或最小化js定時(shí)器是否準(zhǔn)時(shí)測(cè)試
前言
這是我最近開發(fā)碰到的一個(gè)問題,本文是我測(cè)試出來(lái)的實(shí)踐結(jié)果,供大家參考。
關(guān)于js定時(shí)器,setInterval和setTimeout,作為我們?nèi)粘i_發(fā)經(jīng)常使用到的方法,大家一定非常熟悉。比如下面一個(gè)例子:
setInterval(() => {
console.log('1');
}, 500);
作為剛學(xué)前端沒多久的新人也能知道,這段代碼就是每過500ms打印一次1(實(shí)際運(yùn)行還需要考慮js的宏任務(wù)和微任務(wù)的執(zhí)行時(shí)間,定時(shí)器的間隔時(shí)間是500ms,但是定時(shí)器中的方法觸發(fā)可能需要在宏任務(wù)隊(duì)列中排隊(duì),不一定會(huì)在500ms的時(shí)候觸發(fā),關(guān)于Event Loop的基礎(chǔ)內(nèi)容不在本文討論之內(nèi))。
但是如果你把瀏覽器從當(dāng)前頁(yè)面切換到另一個(gè)標(biāo)簽頁(yè),或者把瀏覽器最小化了,這時(shí)候,這個(gè)頁(yè)面定時(shí)器的間隔時(shí)間還是500ms?
本文將測(cè)試setInterval、setTimeout、requestAnimationFrame這三個(gè)方法在瀏覽器可見以及不可見狀態(tài)下的表現(xiàn),我的測(cè)試瀏覽器以及版本是谷歌(86.0.4240.193),火狐(81.0.2),ie11。
瀏覽器可見和不可見狀態(tài)
瀏覽器的可見和不可見狀態(tài)的切換會(huì)觸發(fā)visibilitychange事件,我們可以通過監(jiān)聽這個(gè)事件來(lái)判別瀏覽器的可見狀態(tài)。
document.addEventListener("visibilitychange", function() {
console.log(document.visibilityState);
});
document.visibilityState有三個(gè)值
- hidden:頁(yè)面徹底不可見。
- visible:頁(yè)面至少一部分可見。
- prerender:頁(yè)面即將或正在渲染,處于不可見狀態(tài)。 這里重點(diǎn)關(guān)注hidden這個(gè)值,當(dāng)我們?yōu)g覽器切換當(dāng)前頁(yè)面到另外一個(gè)標(biāo)簽頁(yè)或者把瀏覽器最小化的時(shí)候,document.visibilityState就會(huì)是hidden值。我們也可以使用document.hidden,它返回一個(gè)布爾值,為true的時(shí)候,說明當(dāng)前瀏覽器是不可見狀態(tài)。
關(guān)于visibilitychange的細(xì)節(jié)可以看阮一峰老師的這篇文章 Page Visibility API 教程。
setInterval
我們先來(lái)測(cè)試setInterval,代碼如下
<button id="btn">開始計(jì)時(shí)</button>
// 兼容ie寫法
document.getElementById('btn').addEventListener('click', function() {
setInterval(function() {
const myDate = new Date();
const currentDate = myDate.getMinutes() + '分'+ myDate.getSeconds() + '秒' + myDate.getMilliseconds() + '豪秒';
// 每次循環(huán)打印當(dāng)前時(shí)間
console.log(currentDate);
}, 500);
});
// 瀏覽器可見狀態(tài)切換事件
document.addEventListener('visibilitychange', function() {
if(document.hidden) {
console.log('頁(yè)面不可見');
}
});
定時(shí)器間隔是500ms,先來(lái)看下谷歌瀏覽器

我們發(fā)現(xiàn),當(dāng)頁(yè)面不可見之后,定時(shí)器的間隔變成了1s。 接下來(lái),我們把定時(shí)器間隔改成2s來(lái)試下。

前后間隔時(shí)間一致。
接下來(lái)測(cè)試一下火狐和ie。這里列出的圖片都是500ms和2s的例子。


ie瀏覽器

經(jīng)過我大量的測(cè)試,可以得出結(jié)論,谷歌瀏覽器中,當(dāng)頁(yè)面處于不可見狀態(tài)時(shí),setInterval的最小間隔時(shí)間會(huì)被限制為1s?;鸷鼮g覽器的setInterval和谷歌特性一致,但是ie瀏覽器沒有對(duì)不可見狀態(tài)時(shí)的setInterval進(jìn)行性能優(yōu)化,不可見前后間隔時(shí)間不變。
setTimeout
接下來(lái)是setTimeout
function timer() {
setTimeout(function() {
const myDate = new Date();
const currentDate = myDate.getMinutes() + '分'+ myDate.getSeconds() + '秒' + myDate.getMilliseconds() + '豪秒';
console.log(currentDate);
timer();
}, 500)
}
// 兼容ie寫法
document.getElementById('btn').addEventListener('click', function() {
timer();
});
同樣先來(lái)看看在谷歌瀏覽器中的表現(xiàn)(還是500ms和2s)


我們發(fā)現(xiàn)在谷歌瀏覽器中,500ms的間隔,setTimeout和setInterval表現(xiàn)一致,都是最小間隔限制為1s。但是2s隔間的測(cè)試結(jié)果出現(xiàn)了分歧,頁(yè)面不可見之后,間隔變成了3s。繼續(xù)經(jīng)過多次的測(cè)試,如下,左圖的間隔時(shí)間為990ms,右圖的間隔時(shí)間為1s。


不可見狀態(tài)下,左圖中的990ms間隔時(shí)間變?yōu)?s,右圖中的1s間隔時(shí)間變?yōu)?s。
我們?cè)賮?lái)看看火狐(500ms和2s)


火狐瀏覽器不可見狀態(tài)下,左圖中的500ms變?yōu)?s,右圖中的2s保持不變。
再來(lái)看看ie瀏覽器(500ms)

一樣毫無(wú)優(yōu)化。
我們可以得出結(jié)論
- 在谷歌瀏覽器中,setTimeout在瀏覽器不可見狀態(tài)下間隔低于1s的會(huì)變?yōu)?s,大于等于1s的會(huì)變成N+1s的間隔值。
- 火狐瀏覽器下setTimeout的最小間隔時(shí)間會(huì)變?yōu)?s,大于等于1s的間隔不變。ie瀏覽器在不可見狀態(tài)前后的間隔時(shí)間不變。
requestAnimationFrame
raf是瀏覽器提供的一個(gè)更流暢的處理動(dòng)畫的方法,它會(huì)在下次瀏覽器GUI繪制頁(yè)面的時(shí)候運(yùn)行傳入的方法。GUI繪制頁(yè)面的頻率跟顯示器的刷新率有關(guān),普通顯示器的刷新率是60hz,因此raf在一秒之內(nèi)需要運(yùn)行60次,間隔四舍五入大概是17ms。
function timer() {
const myDate = new Date();
const currentDate = myDate.getMinutes() + '分'+ myDate.getSeconds() + '秒' + myDate.getMilliseconds() + '豪秒';
console.log(currentDate);
window.requestAnimationFrame(timer)
}
// 兼容ie寫法
document.getElementById('btn').addEventListener('click', function() {
timer();
});
我們來(lái)看看不同瀏覽器下面的表現(xiàn):
谷歌瀏覽器

火狐瀏覽器

ie瀏覽器

我們可以發(fā)現(xiàn),谷歌瀏覽器和ie瀏覽器當(dāng)瀏覽器狀態(tài)為不可見時(shí),raf方法將停止執(zhí)行。火狐瀏覽器當(dāng)狀態(tài)變?yōu)椴豢梢姇r(shí),會(huì)在間隔是1s,2s,4s,8s,16s,32s...這樣的順序下去執(zhí)行raf方法。
總結(jié)
谷歌瀏覽器中,當(dāng)頁(yè)面處于不可見狀態(tài)時(shí),setInterval的最小間隔時(shí)間會(huì)被限制為1s?;鸷鼮g覽器的setInterval和谷歌特性一致。ie瀏覽器沒有對(duì)不可見狀態(tài)時(shí)的setInterval進(jìn)行性能優(yōu)化,不可見前后間隔時(shí)間不變。
在谷歌瀏覽器中,setTimeout在瀏覽器不可見狀態(tài)下間隔低于1s的會(huì)變?yōu)?s,大于等于1s的會(huì)變成N+1s的間隔值?;鸷鼮g覽器下setTimeout的最小間隔時(shí)間會(huì)變?yōu)?s,大于等于1s的間隔不變。ie瀏覽器在不可見狀態(tài)前后的間隔時(shí)間不變。
谷歌瀏覽器和ie瀏覽器當(dāng)瀏覽器狀態(tài)為不可見時(shí),raf方法將停止執(zhí)行?;鸷鼮g覽器當(dāng)狀態(tài)變?yōu)椴豢梢姇r(shí),會(huì)在間隔是1s,2s,4s,8s,16s,32s...這樣的順序下去執(zhí)行raf方法。
如何解決
碰到問題當(dāng)然需要解決,在一些定時(shí)器小于1s的倒計(jì)時(shí)的頁(yè)面中,如果用戶切換到了其他標(biāo)簽頁(yè)。再切回去的時(shí)候,頁(yè)面上顯示的倒計(jì)時(shí)時(shí)間其實(shí)是錯(cuò)誤的,這種隱藏的bug會(huì)帶來(lái)很大的風(fēng)險(xiǎn)。該怎么解決呢?
除了調(diào)取后臺(tái)接口或者websocket連接之外,其實(shí)有一個(gè)更好的解決方案,webWorkers。而且webWorkers還可以解決一個(gè)頁(yè)面存在多個(gè)定時(shí)器時(shí)候間隔時(shí)間誤差較大的問題。
直接上例子
document.getElementById('btn').addEventListener('click', function() {
var w = new Worker('demo_workers.js');
w.onmessage = function(event){
console.log(event.data);
};
});
//瀏覽器切換事件
document.addEventListener('visibilitychange', function() {
if(document.hidden) {
console.log('頁(yè)面不可見');
}
});
// demo_workers.js
setInterval(function() {
const myDate = new Date();
const currentDate = myDate.getMinutes() + '分'+ myDate.getSeconds() + '秒' + myDate.getMilliseconds() + '豪秒';
postMessage(currentDate);
}, 500);
實(shí)際結(jié)果


間隔保持一致。
以上就是瀏覽器切換到其他標(biāo)簽頁(yè)或最小化js定時(shí)器是否準(zhǔn)時(shí)測(cè)試的詳細(xì)內(nèi)容,更多關(guān)于瀏覽器切換js定時(shí)器準(zhǔn)時(shí)測(cè)試的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
lodash內(nèi)部方法getFuncName及setToString剖析詳解
本篇章我們主要是通過了解lodash里的兩個(gè)內(nèi)部方法getFuncName方法和setToString方法,在實(shí)際開發(fā)中我們也可以借鑒方法的實(shí)現(xiàn)思路,在需要的時(shí)候簡(jiǎn)單封裝一下,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
微信小程序 數(shù)據(jù)交互與渲染實(shí)例詳解
這篇文章主要介紹了微信小程序 數(shù)據(jù)交互與渲染實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-01-01
前端利用jsencrypt.js進(jìn)行RSA加密示例詳解
這篇文章主要為大家介紹了前端利用jsencrypt.js進(jìn)行RSA加密示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
瀏覽器切換到其他標(biāo)簽頁(yè)或最小化js定時(shí)器是否準(zhǔn)時(shí)測(cè)試
這篇文章主要為大家介紹了瀏覽器切換到其他標(biāo)簽頁(yè)或最小化是js定時(shí)器是否準(zhǔn)時(shí)的測(cè)試詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
前端取消請(qǐng)求及取消重復(fù)請(qǐng)求方式
這篇文章主要為大家介紹了前端取消請(qǐng)求及取消重復(fù)請(qǐng)求方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
創(chuàng)建圖片對(duì)比slider滑塊示例詳解
這篇文章主要為大家介紹了創(chuàng)建圖片對(duì)比slider滑塊示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Astro Islands靜態(tài)頁(yè)面交互式UI組件
這篇文章主要為大家介紹了Astro Islands靜態(tài)頁(yè)面交互式UI組件使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08

