# 為何你的 Interval 會越跑越慢?閒置狀態頁籤中的 setTimeout、setInterval 發生什麼事 #### 瀏覽器為了避免閒置頁籤浪費資源的種種節流 ### ❓ 前端用 setInterval 寫的時鐘,過一陣子回來越跑越慢了,為什麼? 在背景頁籤中執行的 Timer 會默默消耗莫大的資源,沒在使用的閒置頁籤甚至影響到正在使用的頁籤,為了避免不必要的資源浪費,瀏覽器會針對閒置狀態等頁籤的 Timer 做節流 在 Chrome 上,<span class="blue">無論程式碼中設定的 Interval、Timeout 時間為多少,Chrome 都會強制設定為至少一秒。</span>意指如果你原本程式中寫的是 setInterval() 每 50ms 執行一次,但只要進入其他頁籤,讓該頁籤進入背景時,該 Interval 執行時間會節流至<span class="blue"> 每 1000ms </span>執行一次,<span class="blue">但是只要重新點回該頁籤,Timer 的節流又會取消</span> 在 2021年1月 時,<span class="blue">Chrome 88 </span>以上的版本甚至在特定條件下,該節流時間會被拉長到 <span class="blue">1分鐘</span> 一些後面會提到的術語也先做基本講解: * **隱藏頁籤** 代表有其他頁籤現在是正在被可見的,也就是說使用者切換到其他頁籤,或是瀏覽器被縮到最小化。而瀏覽器的頁籤是能夠透過 [Page visibility api](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) 得知的 * **Timer** 代表 Javascript 的 `setTimeout` 以及 `setInterval` ,可以透過 Timer 來安排未來要執行的事件 * **連鎖 Timer** 代表 setTimeout 或 setInterval 是接連執行的,例如 每秒更新時鐘時間 ``` setInterval(() => { clockTime ++; }, 1000); ``` ### 💻 Chrome 節流條件?節流影響? 節流分階段性: #### 🔒 最小節流 只要滿足下述任何條件,就只會有最小程度的節流 * 網頁是可見的(非隱藏頁籤) * 在過去 <span class="blue">30秒</span> 內,網頁有透過任何 發聲API 製造過聲音(無聲的音軌除外) 在這階段下基本上瀏覽器不會做任何節流,除非你的 Timer 的<span class="blue">時間設定低於 4ms,且連鎖次數大於5次時</span>,瀏覽器會將時間設定為至少 4ms #### 🔒 一般節流 這種情況發生於 你的 Timer 不滿足上述情況(意指你的頁籤不為可見狀態,且在過去30秒內頁籤也沒有製造做任何聲音),且任何下述條件成立滿足時: * Timer 連鎖次數低於五次 * 頁籤處在隱藏狀態,但時間低於五分鐘 * 頁籤正在使用 WebRTC(網頁間實現音頻、數據通信的技術) 在這階段下,瀏覽器會節流至每 <span class="blue">1秒</span> 才執行一次。時間接近的 Timer 則會批次一起被進行 #### 🔒 高強度的節流 這部分是 Chrome 88 開始才有的新內容,當你的 Timer 已經不滿足上述兩個節流階段(例如瀏覽器已經閒置至少五分鐘),且滿足以下<span class="blue">所有條件</span>時會發生: * 頁籤已經處於隱藏狀態超過五分鐘 * Timer 連鎖數量超過五次 * 該頁籤已經至少30秒沒發過任何聲音 * 沒有使用 WebRTC 在這階段下,瀏覽器會節流至每 <span class="blue">1分鐘</span> 才執行一次 ### 🔨 實測 在執行以下程式碼時,我預期每秒鐘會印一次當前時間 ``` javascript setInterval(()=>{ const currentTime = new Date().toLocaleTimeString(); console.log(currentTime) }, 1000) ``` 1. 一開始會很正常的每秒印出時間  1. 但當頁籤處在隱藏狀態超過一分鐘後,瀏覽器開始節流,變為 每分鐘執行一次  3. 後面皆為每分鐘執行一次  4. 直到 上午 1:08:52 點回該頁籤,頁籤重新成為可見狀態,時間立刻變回一秒印一次  ### 📌 Workarounds * **Web workers** [Web workers](https://developer.mozilla.org/zh-TW/docs/Web/API/Web_Workers_API/Using_web_workers) 是一種允許網頁在背景[執行緒](https://levelup.gitconnected.com/how-web-browsers-use-processes-and-threads-9f8f8fa23371) 運行的機制,且可以不阻塞主線程正常運行。從[這篇](https://medium.com/@adithyaviswam/overcoming-browser-throttling-of-setinterval-executions-45387853a826)文章所說,上述節流的機制僅會進行在瀏覽器的主執行緒上,所以透過讓 Timer 在 Web workers執行緒上運行則能避免被節流(能看此 [Demo](https://w2or0.csb.app/),利用 Web workers 的 Timer 與 沒有 在 Web workers 上運行的 Timer 差距會越來越大)  * **監聽頁籤狀態** 如果為了避免瀏覽器的 Timer 被節流進而影響到網頁的話,可以在頁籤進入背景時,可以透過監聽原生 Web API 事件 [visibilitychange](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/visibilitychange_event),預先將相關 Timer 中斷 ``` document.addEventListener('visibilitychange', function() { if(document.hidden) { // 頁籤進入背景,把相關 Timer 中斷 } else { // 頁籤進入重新啟用,將 Timer 重啟 } }); ``` ### 參考文獻: * [Overcoming browser throttling of setInterval executions](https://medium.com/@adithyaviswam/overcoming-browser-throttling-of-setinterval-executions-45387853a826) * [Heavy throttling of chained JS timers beginning in Chrome 88](https://developer.chrome.com/blog/timer-throttling-in-chrome-88/) * [What Happens to setTimeout() / setInterval() Timers Running on Inactive Browser Tabs ?](https://usefulangle.com/post/280/settimeout-setinterval-on-inactive-tab) * [https://usefulangle.com/web-updates/post/110/chrome-88-will-throttle-timers-1-minute](https://usefulangle.com/web-updates/post/110/chrome-88-will-throttle-timers-1-minute) <style> .blue { color: #409EFF; } </style>
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.