iT邦幫忙

2025 iThome 鐵人賽

DAY 25
1

前情提要

在前一篇原子交易(Atomic Transaction)中,我們已經看過交易如何確保狀態一致性。
這一篇要更深入探討 Scheduler(調度器)的設計,說明不同策略的取捨,並對照我們的程式碼在流程圖中落在哪條路徑。

Scheduler 的核心概念

Scheduler 是 reactivity 系統中的「隱形指揮官」

  • 它決定 更新任務 什麼時候執行。
  • 它影響 效能開發者心智模型

簡單來說,Scheduler 不是「要不要算」,而是「何時算」。

調度策略的分類

大致可以分為以下幾類:

同步調度 (Synchronous Scheduling)

  • 特點:資料更新後,立即執行對應的副作用。
  • 優點:邏輯直觀,狀態變動即刻反映。
  • 缺點:容易產生過度運算,特別在短時間多次更新時,會浪費大量資源。

批次調度 (Batch Scheduling)

  • 特點:將同一個 tick 內的更新合併,只在最後執行一次副作用。
  • 優點:減少重複運算,提升效能。
  • 缺點:需要一層「任務緩衝」機制,增加心智複雜度。

優先級調度 (Priority Scheduling)

  • 特點:任務依照優先級排序,例如:
    • 使用者輸入相關的更新 → 高優先級
    • 背景資料同步 → 低優先級
  • 優點:避免低價值的更新阻塞高價值互動。
  • 缺點:系統實作較複雜,需要追蹤「deadline 與優先權」。

Lazy vs. Eager 調度

  • Lazy:只在「必要時」才執行,例如 Pull-based reactivity。
  • Eager:更新一發生就立刻排程後續運算,例如 Push-based。

這種差異會影響系統的整體策略:

  • Lazy 適合「讀多寫少」的情境。
  • Eager 適合「寫少讀多」的情境。

Scheduler 策略比較流程圖

https://ithelp.ithome.com.tw/upload/images/20250828/20129020yR592iHqN5.png

這張圖展示了從「資料更新」到「UI 更新」的不同策略路徑。

策略比較表

策略 觸發時機 核心資料流 優點 代價 / 風險 適用情境 常見實例
同步調度 (Sync) set() 後立即執行 write → compute → effect 心智直觀、除錯容易 重複運算、互動抖動 極簡頁面、更新頻率低 小型框架、教學用 PoC
批次調度 (Batch) 同一 tick/microtask 末尾一次 flush write × N → queue → flush 減少重繪、效能佳 延遲一致、需要 queue 表單互動、動畫外層 React batched updates、Vue job queue
優先級調度 (Priority) 依 deadline / 重要性切片 enqueue(prio) → run until deadline 不阻塞高價值互動 複雜度高、可能「飢餓」 長列表、並行渲染 React Concurrent、startTransition
Lazy 被讀取時才計算 write → mark-dirty → read 才算 寫入代價低 首次讀取延遲 讀多寫少 Signals memo/computed
Eager 寫入當下標記下游 write → mark downstream → flush 讀取更快、失效清楚 寫入代價高 寫少讀多、強一致 UI Solid/Preact Signals

框架對 Scheduler 的實作比較

框架 調度模式 策略
React Batch + Priority microtask queue + concurrent
Vue 3 Batch job queue
Solid Microtask Batch (Eager) 每個更新皆標記並批次刷新
Signals (Preact/Angular) Push + Lazy 僅在讀取時才計算依賴

可以看出不同框架對 Scheduler 的取捨,直接影響到「效能優化」與「心智模型」的差異。

Atomic Transaction 與流程圖對應

在 Atomic Transaction 章節,我們有三個重要情境:

1. 成功路徑:begin → writes → commit

  • 流程圖:資料更新 → 批次調度 + Eager 標記 → flush → 副作用
  • 策略:Batch + Eager

2. 失敗路徑:begin → writes → rollback

  • 流程圖:資料更新 → (尚未 flush) → 回滾 → 標髒下游 → 下次讀取才重算
  • 策略:Eager(標記)+ Lazy(推遲重算)

3. 讀取一致性

  • 交易期間讀取值:
    • 封鎖跨交易讀 → commit 前只看到穩定值
    • 允許讀暫存值 → 需快照隔離,避免半完成狀態

API 對照

  • signal.set() → 寫入
  • markStale(node) → Eager 標記
  • track()/link()/unlink() → 依賴追蹤
  • queueMicrotask(flush) → Batch flush
  • flush() → 拓撲重算 + runEffects
  • rollback() → 還原快照 + 標記髒值

實作對齊:atomic() / transaction()

在上一篇中,為了方便大家對照,所以我保留了原本 transaction() 的部分,但其實應該要使用 atomic() 的邏輯取代掉 transaction() 的,從程式碼來看,具備完整交易語意的是 atomic()

  • Begin:建立 write-log,攔截期間所有寫入。
  • Commit:丟棄 write-log,並在同一 tick 末尾批次 flushJobs()(屬於 Batch + Eager)。
  • Rollback:按 write-log 將 signal 值還原,並對其下游 computed 標髒標記,確保下一次讀或下一次成功交易前不會誤用快取。

而目前的 transaction() 僅等同於「批次 (batch)」,缺乏 rollback,語意與前文不同。

修改調整

  1. 要讓 transaction 與前文語意一致,建議直接包裝成 atomic() 的別名:
export function transaction<T>(fn: () => T): T;
export function transaction<T>(fn: () => Promise<T>): Promise<T>;
export function transaction<T>(fn: () => T | Promise<T>): T | Promise<T> {
  return atomic(fn);
}
  1. scheduleJob() 加入 muted 判斷,避免 rollback 期間排進新任務:
export function scheduleJob(job: Schedulable) {
  if (job.disposed) return;
  if (muted > 0) return; // 回滾/靜音期間不進隊列
  queue.add(job);
  if (!scheduled && batchDepth === 0) {
    scheduled = true;
    queueMicrotask(flushJobs);
  }
}

其他部分不需要更動,改了這個只是讓行為跟上述策略描述上對齊。

Scheduler 與開發者心智模型

一個有趣的問題是:

開發者究竟需不需要知道 Scheduler 的存在?

  • 在 React 中,開發者常常需要考慮「state 更新並非立即生效」,因此心智模型必須包含 batch 的概念。
  • 在 Solid / Signals 系統中,因為運算是細顆粒度,開發者幾乎感受不到調度機制的存在,僅需專注在資料本身。

因此,我們可以這樣總結:
Scheduler 不僅是效能優化的工具,也是一種「開發者體驗」的設計。

展望與進一步思考

進階的 Scheduler 可能還會包含:

  • Idle 調度:利用空閒時間處理非即時任務。
  • 工作分片 (Work Stealing):在多執行緒/多核心環境分配任務。
  • 自適應調度 (Adaptive Scheduling):根據裝置性能或使用者互動模式動態調整。

這些設計概念其實與作業系統的「排程器」如出一轍,只是應用在前端 reactivity 的脈絡中。

結語

Scheduler 在 reactivity 系統中,扮演著「隱形指揮官」的角色。
它不僅決定了效能的上限,也影響了開發者的心智模型。

  • React 選擇了「優先級 + 批次」
  • Vue 選擇了「批次任務隊列」
  • Solid 選擇了「細顆粒度 eager 更新」

在不同框架的取捨背後,其實都圍繞著同一個問題:
「我們希望在什麼時機,付出多少代價,去換取 UI 的即時一致性?」

下一篇,我們來談談 Scheduler 在記憶體與圖管理上面臨的議題。


上一篇
進階內核(II):原子交易
下一篇
進階內核(IV):記憶體與圖管理
系列文
Reactivity 小技巧大變革:掌握 Signals 就這麼簡單!27
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言