Try   HackMD

為何你的 Interval 會越跑越慢?閒置狀態頁籤中的 setTimeout、setInterval 發生什麼事

瀏覽器為了避免閒置頁籤浪費資源的種種節流

❓ 前端用 setInterval 寫的時鐘,過一陣子回來越跑越慢了,為什麼?

在背景頁籤中執行的 Timer 會默默消耗莫大的資源,沒在使用的閒置頁籤甚至影響到正在使用的頁籤,為了避免不必要的資源浪費,瀏覽器會針對閒置狀態等頁籤的 Timer 做節流

在 Chrome 上,無論程式碼中設定的 Interval、Timeout 時間為多少,Chrome 都會強制設定為至少一秒。意指如果你原本程式中寫的是 setInterval() 每 50ms 執行一次,但只要進入其他頁籤,讓該頁籤進入背景時,該 Interval 執行時間會節流至 每 1000ms 執行一次,但是只要重新點回該頁籤,Timer 的節流又會取消

在 2021年1月 時,Chrome 88 以上的版本甚至在特定條件下,該節流時間會被拉長到 1分鐘

一些後面會提到的術語也先做基本講解:

  • 隱藏頁籤
    代表有其他頁籤現在是正在被可見的,也就是說使用者切換到其他頁籤,或是瀏覽器被縮到最小化。而瀏覽器的頁籤是能夠透過 Page visibility api 得知的

  • Timer
    代表 Javascript 的 setTimeout 以及 setInterval ,可以透過 Timer 來安排未來要執行的事件

  • 連鎖 Timer
    代表 setTimeout 或 setInterval 是接連執行的,例如 每秒更新時鐘時間

    ​​​​setInterval(() => {
    ​​​​  clockTime ++;
    ​​​​}, 1000);
    

💻 Chrome 節流條件?節流影響?

節流分階段性:

🔒 最小節流

只要滿足下述任何條件,就只會有最小程度的節流

  • 網頁是可見的(非隱藏頁籤)
  • 在過去 30秒 內,網頁有透過任何 發聲API 製造過聲音(無聲的音軌除外)

在這階段下基本上瀏覽器不會做任何節流,除非你的 Timer 的時間設定低於 4ms,且連鎖次數大於5次時,瀏覽器會將時間設定為至少 4ms

🔒 一般節流

這種情況發生於 你的 Timer 不滿足上述情況(意指你的頁籤不為可見狀態,且在過去30秒內頁籤也沒有製造做任何聲音),且任何下述條件成立滿足時:

  • Timer 連鎖次數低於五次
  • 頁籤處在隱藏狀態,但時間低於五分鐘
  • 頁籤正在使用 WebRTC(網頁間實現音頻、數據通信的技術)

在這階段下,瀏覽器會節流至每 1秒 才執行一次。時間接近的 Timer 則會批次一起被進行

🔒 高強度的節流

這部分是 Chrome 88 開始才有的新內容,當你的 Timer 已經不滿足上述兩個節流階段(例如瀏覽器已經閒置至少五分鐘),且滿足以下所有條件時會發生:

  • 頁籤已經處於隱藏狀態超過五分鐘
  • Timer 連鎖數量超過五次
  • 該頁籤已經至少30秒沒發過任何聲音
  • 沒有使用 WebRTC

在這階段下,瀏覽器會節流至每 1分鐘 才執行一次

🔨 實測

在執行以下程式碼時,我預期每秒鐘會印一次當前時間

setInterval(()=>{
    const currentTime = new Date().toLocaleTimeString();
    console.log(currentTime)
}, 1000)
  1. 一開始會很正常的每秒印出時間
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
  2. 但當頁籤處在隱藏狀態超過一分鐘後,瀏覽器開始節流,變為
    每分鐘執行一次
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
  3. 後面皆為每分鐘執行一次
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
  4. 直到 上午 1:08:52 點回該頁籤,頁籤重新成為可見狀態,時間立刻變回一秒印一次
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

📌 Workarounds

  • Web workers
    Web workers 是一種允許網頁在背景執行緒 運行的機制,且可以不阻塞主線程正常運行。從這篇文章所說,上述節流的機制僅會進行在瀏覽器的主執行緒上,所以透過讓 Timer 在 Web workers執行緒上運行則能避免被節流(能看此 Demo,利用 Web workers 的 Timer 與 沒有 在 Web workers 上運行的 Timer 差距會越來越大)

    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

  • 監聽頁籤狀態
    如果為了避免瀏覽器的 Timer 被節流進而影響到網頁的話,可以在頁籤進入背景時,可以透過監聽原生 Web API 事件 visibilitychange,預先將相關 Timer 中斷

    ​​​​document.addEventListener('visibilitychange', function() {
    ​​​​    if(document.hidden) {
    ​​​​       // 頁籤進入背景,把相關 Timer 中斷
    ​​​​    }
    ​​​​    else {
    ​​​​       // 頁籤進入重新啟用,將 Timer 重啟
    ​​​​    }
    ​​​​});
    

參考文獻: