Featured image of post 使用 requestIdleCallback 執行非阻塞腳本Featured image of post 使用 requestIdleCallback 執行非阻塞腳本

使用 requestIdleCallback 執行非阻塞腳本

了解如何使用 requestIdleCallback API 在瀏覽器空閒期間執行低優先權後台任務,從而保留互動幀速率。

介紹

在建立複雜的 Web 應用程式時,保持流暢的動畫和響應式使用者輸入(理想情況下匹配 60+ FPS 幀速率)對於良好的使用者體驗至關重要。

然而,現代應用程式通常需要執行低優先順序的後台操作,例如發送分析資料、同步快取、解析遙測日誌或預取資產。

作為標準非同步承諾或立即計時器執行這些非緊急任務可能會阻塞主線程中幀,導致介面卡頓或增加下次繪製交互 (INP) 分數。

requestIdleCallback API 解決了這個問題。本文回顧了 API 的工作原理並分享了生產程式碼庫的實際用例。


1.什麼是requestIdleCallback?

requestIdleCallback 是一個本機瀏覽器 API,允許您對低優先權後台任務進行排隊,以便在瀏覽器空閒期間(主執行緒完成渲染更新並等待使用者輸入時的間隙)運行。

瀏覽器渲染週期

通常,瀏覽器會嘗試在單一畫面(大約 16.6 毫秒)內執行下列步驟:

[Single Frame (approx 16.6ms)]
 ├─► 1. Process user inputs (clicks, keypresses)
 ├─► 2. Execute active macro-tasks (setTimeout/setInterval)
 ├─► 3. Run requestAnimationFrame updates
 ├─► 4. Recalculate layout & paint pixels
 └─► 5. 【Browser Idle Period】 ◄── requestIdleCallback runs here!

如果瀏覽器提前完成渲染更新,它將使用剩餘時間來執行 requestIdleCallback 中排隊的任務。如果發生高優先權事件(例如按一下或觸控輸入),瀏覽器會立即將控制權交還給主渲染循環,以保持 UI 回應。


2. 基本設定和語法

標準實施

// A low-priority task queue processor
function sendAnalyticsData(deadline) {
  // deadline.timeRemaining() returns the milliseconds left in the current idle frame
  while (deadline.timeRemaining() > 0 && tasks.length > 0) {
    const task = tasks.shift();
    executeTask(task);
  }

  // If tasks remain, queue them for the next idle period
  if (tasks.length > 0) {
    requestIdleCallback(sendAnalyticsData);
  }
}

// Queue the callback
requestIdleCallback(sendAnalyticsData);

實作逾時選項

如果瀏覽器始終處於忙碌狀態,則在 requestIdleCallback 中排隊的任務可能會無限期延遲。

為了防止這種情況,您可以設定 timeout 選項。這告訴瀏覽器在指定的截止日期之前執行回調,即使它必須在繁忙的幀期間運行。

// Schedule execution, but force it to run if 2 seconds (2000ms) pass
requestIdleCallback(sendAnalyticsData, { timeout: 2000 });

3. 實際用例

requestIdleCallback 對於不會立即變更活動 DOM 結構的非視覺任務很有用:

① 批次遙測和分析發送

不要立即為每個使用者點擊觸發 HTTP 請求,而是將遙測積分儲存在本機數組中,並在瀏覽器空閒時批量發送它們。

② 同步客戶端存儲

使用它來寫入日誌、清理過期的索引鍵或將客戶端資料同步到 localStorageIndexedDB ,而不會阻止使用者輸入。

③ 預取資產和組件

在空閒期間預先載入大量程式碼或下游影像,以便當使用者導航到新頁面時它們已準備好。


重要警告:避免 DOM 操作

切勿修改 requestIdleCallback 函數內的 DOM。

更新 DOM 會強制瀏覽器重新計算頁面佈局(回流)。這會使活動空閒狀態無效,並可能導致佈局抖動。如果您需要根據空閒任務進行視覺更改,請將 DOM 更新傳遞給 requestAnimationFrame

requestIdleCallback((deadline) => {
  const result = computeData(deadline);

  // Schedule DOM writes on the animation thread
  requestAnimationFrame(() => {
    element.textContent = result;
  });
});

結論

使用 requestIdleCallback 透過將非緊急任務轉移到瀏覽器空閒時間來幫助您最佳化頁面效能。

  1. **使用 requestIdleCallback 將非緊急後台任務延後到空閒訊框。 **
  2. **使用 timeout 參數可防止任務無限期延遲。 **
  3. **避免直接在空閒回呼中執行 DOM 寫入。 **

將此 API 合併到您的應用程式中,讓您的 UI 感覺能夠響應用戶輸入。