在前一篇,我們介紹了 優先級與分層 Scheduler,解決了「重要任務先跑」的問題。
然而,在實際應用中,我們還會遇到另一個挑戰:長任務會阻塞主執行緒。
為了讓使用者始終感覺流暢,Scheduler 必須支援 Time-Slicing (時間切片) 與 Cooperative Scheduling (協作式排程)。
想像一個場景:
UI thread 正在執行一個 大型渲染任務(例如一次更新 5000 筆列表項目),這段程式可能需要數十毫秒,甚至超過 100ms 才能完成。
在這段時間內:
光靠「優先級」不夠,因為任務一旦進入執行階段,仍然可能「獨佔主執行緒」。
Time-Slicing 的核心思想是:
將一個長任務切分成小片段,允許在片段之間釋放主執行緒。
這樣可以確保:
在作業系統裡,有 Preemptive Scheduling (搶佔式) 與 Cooperative Scheduling (協作式)。
JavaScript 單執行緒的世界裡,沒有辦法真正「搶佔」。
shouldYield()
)這就是 React Concurrent Mode 採用的策略。
這是一個簡化的「時間切片 + 協作式」 Scheduler:
let deadline = 0;
function shouldYield() {
return performance.now() >= deadline;
}
export function runWithTimeSlicing<T>(work: () => T, timeSlice = 5): T | void {
deadline = performance.now() + timeSlice;
while (!shouldYield()) {
const result = work();
return result;
}
// 沒做完 → 延後執行
queueMicrotask(() => runWithTimeSlicing(work, timeSlice));
}
在 分層 + 優先級 Scheduler 的基礎上,我們還能結合 Time-Slicing:
requestIdleCallback
,只在瀏覽器空閒時處理這樣就能兼顧即時互動、背景大任務及整體流暢度。
優先級 解決了「先處理誰」的問題,
Time-Slicing 與協作式排程 則解決了「如何避免卡頓」的問題。
這兩者結合,讓 Signal 系統或 React/Vue 這類框架,能在面對 龐大更新量 時仍保持流暢。
下一篇,我們會探討「DevTools 與診斷」包括 inspect 節點、依賴圖可視化、render 計數器、熱點追蹤等工具,如何幫助我們理解 signal 與 scheduler 在真實應用中的行為。