前言
很多的問題就在實踐中得到解決。
本文主要說的就是js定時器,setInterval和setTimeout,作為我們?nèi)粘i_發(fā)經(jīng)常使用到的方法。我們先給大家下面一個例子:
setInterval(() => { console.log('1'); }, 500);
這段代碼就是每過500ms打印一次1(實際運行還需要考慮js的宏任務(wù)和微任務(wù)的執(zhí)行時間,定時器的間隔時間是500ms,但是定時器中的方法觸發(fā)可能需要在宏任務(wù)隊列中排隊,不一定會在500ms的時候觸發(fā),關(guān)于Event Loop的基礎(chǔ)內(nèi)容不在本文討論之內(nèi))。
但要是從當(dāng)前頁面切換到另一個標(biāo)簽頁,或者把瀏覽器最小化了,這時候,這個頁面定時器的間隔時間還是500ms?
本文將測試setInterval、setTimeout、requestAnimationFrame這三個方法在瀏覽器可見以及不可見狀態(tài)下的表現(xiàn),我的測試瀏覽器以及版本是谷歌(86.0.4240.193),火狐(81.0.2),ie11。
瀏覽器可見和不可見狀態(tài)
我要知道瀏覽器的可見和不可見狀態(tài)的切換會觸發(fā)visibilitychange事件,這樣就可以通過監(jiān)聽這個事件來判別瀏覽器的可見狀態(tài)。
document.addEventListener("visibilitychange", function() { console.log(document.visibilityState); });
document.visibilityState有三個值
hidden:頁面徹底不可見。
visible:頁面至少一部分可見。
prerender:頁面即將或正在渲染,處于不可見狀態(tài)。要知道重點關(guān)注hidden,主要就是它可以在瀏覽器切換當(dāng)前頁面到另外一個標(biāo)簽頁或者把瀏覽器最小化的時候,document.visibilityState就會是hidden值。由此可以使用document.hidden,它返回一個布爾值,為true的時候,說明當(dāng)前瀏覽器是不可見狀態(tài)。
setInterval
我們先來測試setInterval,代碼如下
<button id="btn">開始計時</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)前時間 console.log(currentDate); }, 500); }); // 瀏覽器可見狀態(tài)切換事件 document.addEventListener('visibilitychange', function() { if(document.hidden) { console.log('頁面不可見'); } });
定時器間隔是500ms,谷歌瀏覽器如下
我們發(fā)現(xiàn),當(dāng)頁面不可見之后,定時器的間隔變成了1s。接下來,我們把定時器間隔改成2s來試下。
前后間隔時間一致。
接下來測試一下火狐和ie。這里列出的圖片都是500ms和2s的例子。
ie瀏覽器
在不斷的測試中,谷歌瀏覽器中,當(dāng)頁面處于不可見狀態(tài)時,setInterval的最小間隔時間會被限制為1s。火狐瀏覽器的setInterval和谷歌特性一致,但是ie瀏覽器沒有對不可見狀態(tài)時的setInterval進(jìn)行性能優(yōu)化,不可見前后間隔時間不變。
setTimeout
接下來是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(); });
同樣先來看看在谷歌瀏覽器中的表現(xiàn)(還是500ms和2s)
通過對比,可以知道在谷歌瀏覽器中,500ms的間隔,setTimeout和setInterval表現(xiàn)一致,都是最小間隔限制為1s。但是2s隔間的測試結(jié)果出現(xiàn)了分歧,頁面不可見之后,間隔變成了3s。繼續(xù)經(jīng)過多次的測試,如下,左圖的間隔時間為990ms,右圖的間隔時間為1s。
不可見狀態(tài)下,左圖中的990ms間隔時間變?yōu)?s,右圖中的1s間隔時間變?yōu)?s。
我們再來看看火狐(500ms和2s)
火狐瀏覽器不可見狀態(tài)下,左圖中的500ms變?yōu)?s,右圖中的2s保持不變。
再來看看ie瀏覽器(500ms)
顯而易見并無改動?! ?/p>
在谷歌瀏覽器中,setTimeout在瀏覽器不可見狀態(tài)下間隔低于1s的會變?yōu)?s,大于等于1s的會變成N+1s的間隔值。
火狐瀏覽器下setTimeout的最小間隔時間會變?yōu)?s,大于等于1s的間隔不變。ie瀏覽器在不可見狀態(tài)前后的間隔時間不變。
requestAnimationFrame
raf是瀏覽器提供的一個更流暢的處理動畫的方法,它會在下次瀏覽器GUI繪制頁面的時候運行傳入的方法。GUI繪制頁面的頻率跟顯示器的刷新率有關(guān),普通顯示器的刷新率是60hz,因此raf在一秒之內(nèi)需要運行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(); });
我們來看看不同瀏覽器下面的表現(xiàn):
谷歌瀏覽器
火狐瀏覽器
ie瀏覽器
我們可以發(fā)現(xiàn),谷歌瀏覽器和ie瀏覽器當(dāng)瀏覽器狀態(tài)為不可見時,raf方法將停止執(zhí)行?;鸷鼮g覽器當(dāng)狀態(tài)變?yōu)椴豢梢姇r,會在間隔是1s,2s,4s,8s,16s,32s...這樣的順序下去執(zhí)行raf方法。
匯總
當(dāng)頁面處于不可見狀態(tài)時,谷歌瀏覽器中,當(dāng)頁面處于不可見狀態(tài)時,setInterval的最小間隔時間會被限制為1s。火狐瀏覽器的setInterval和谷歌特性一致。ie瀏覽器沒有對不可見狀態(tài)時的setInterval進(jìn)行性能優(yōu)化,不可見前后間隔時間不變。
在谷歌瀏覽器中,setTimeout在瀏覽器不可見狀態(tài)下間隔低于1s的會變?yōu)?s,大于等于1s的會變成N+1s的間隔值?;鸷鼮g覽器下setTimeout的最小間隔時間會變?yōu)?s,大于等于1s的間隔不變。ie瀏覽器在不可見狀態(tài)前后的間隔時間不變。
谷歌瀏覽器和ie瀏覽器當(dāng)瀏覽器狀態(tài)為不可見時,raf方法將停止執(zhí)行。火狐瀏覽器當(dāng)狀態(tài)變?yōu)椴豢梢姇r,會在間隔是1s,2s,4s,8s,16s,32s...這樣的順序下去執(zhí)行raf方法。
解決方法
在一些定時器小于1s的倒計時的頁面中,如果用戶切換到了其他標(biāo)簽頁。再切回去的時候,頁面上顯示的倒計時時間其實是錯誤的,這可是一個大的bug,要如何解決?
我們處理可以調(diào)取后臺接口或者websocket連接之外,還有一個更好的方法:webWorkers。而且webWorkers還可以解決一個頁面存在多個定時器時候間隔時間誤差較大的問題。
直接上例子
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('頁面不可見'); } });
// demo_workers.js setInterval(function() { const myDate = new Date(); const currentDate = myDate.getMinutes() + '分'+ myDate.getSeconds() + '秒' + myDate.getMilliseconds() + '豪秒'; postMessage(currentDate); }, 500);
實際結(jié)果
間隔保持一致。
很多問題看似在“吹毛求疵”,但何嘗不是一種進(jìn)步。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/128192.html
我們講述的是關(guān)于 ahooks 源碼系列文章的第七篇,總結(jié)主要講述下面幾點: 鞏固 React hooks 的理解?! W(xué)習(xí)如何抽象自定義 hooks。構(gòu)建屬于自己的 React hooks 工具庫。 培養(yǎng)閱讀學(xué)習(xí)源碼的習(xí)慣,工具庫是一個對源碼閱讀不錯的選擇?! ∽ⅲ罕鞠盗袑?ahooks 的源碼解析是基于v3.3.13。自己 folk 了一份源碼,主要是對源碼做了一些解讀,可見詳情?! ?..
摘要:歡迎訪問我的個人博客前言在工作中我們可能會遇到這樣的需求,當(dāng)瀏覽器切換到別的標(biāo)簽頁或著最小化時,我們需要暫停頁面上正在播放的視頻或者音樂,這個需求就會用到我下面要說的這個知識點具體用法瀏覽器標(biāo)簽頁隱藏或者顯示時會改變和的值,我們可以通過這個 歡迎訪問我的個人博客:http://www.xiaolongwu.cn 前言 在工作中我們可能會遇到這樣的需求,當(dāng)瀏覽器切換到別的標(biāo)簽頁或著最小化...
摘要:如果看完本文后,還對進(jìn)程線程傻傻分不清,不清楚瀏覽器多進(jìn)程瀏覽器內(nèi)核多線程單線程運行機(jī)制的區(qū)別。因此準(zhǔn)備梳理這塊知識點,結(jié)合已有的認(rèn)知,基于網(wǎng)上的大量參考資料,從瀏覽器多進(jìn)程到單線程,將引擎的運行機(jī)制系統(tǒng)的梳理一遍。 前言 見解有限,如有描述不當(dāng)之處,請幫忙及時指出,如有錯誤,會及時修正。 ----------超長文+多圖預(yù)警,需要花費不少時間。---------- 如果看完本文后,還...
閱讀 687·2023-03-27 18:33
閱讀 889·2023-03-26 17:27
閱讀 756·2023-03-26 17:14
閱讀 738·2023-03-17 21:13
閱讀 668·2023-03-17 08:28
閱讀 2092·2023-02-27 22:32
閱讀 1521·2023-02-27 22:27
閱讀 2432·2023-01-20 08:28