Execution — fw-04-mini-react-scheduling

Steps extracted from CONCEPTS.md. Read the concepts first; this file is the doing.

Record results in observation.md; tick checkpoints in verification.md.


2. Derivation, not transcription

Work through this in order. Each step should feel forced by the previous one.

  1. Your fw-02 renderer is recursive. Render a tree of 10,000 nodes. Measure the task length. Compare to the frame budget from bi-10. Observe the dropped frames directly.
  2. Try to yield. Attempt to pause halfway through the recursive render and resume later. Discover that you cannot — the state you would need to resume is spread across the call stack.
  3. Therefore: make the stack an explicit data structure. A node with child, sibling, return pointers, walked with a loop instead of recursion. Now "where am I" is a value you can hold.
  4. Now yielding is possible. Loop while work remains and time remains; otherwise schedule a continuation (bi-11: which primitive, and why not microtasks?).
  5. Now you need two phases. Interruptible work must not touch the DOM, because a partially applied update is user-visible. So: a render phase that builds, and a commit phase that applies atomically and cannot be interrupted.
  6. Now you need double buffering. Building the new tree while the current one is displayed means two trees and a pointer swap — an alternate.
  7. Now you can prioritise. Different updates get different deadlines; a high-priority update can abandon in-progress low-priority work. Which forces: work must be discardable, so the render phase must have no side effects.
  8. Batching falls out. Multiple setState calls in one task coalesce into one render, because rendering is scheduled rather than immediate.

At the end, write down what you built. Then read React's Fiber. Every structural element you derived should be recognisable, and anything in React you did not derive is a question worth answering.


3. Failure Lab

  1. Commit interruption. Deliberately yield in the middle of the commit phase. Produce a visibly half-updated UI. This is why commit is atomic.
  2. Side effects in render. Mutate something external during the render phase, then have that render discarded by a higher-priority update. Observe the corruption. This is why render must be pure — and it is a far better explanation than "React is functional."
  3. Yield too often. Measure total time as you shrink the yield interval. Find the point where scheduling overhead dominates.
  4. Yield too rarely. Measure input delay. Find the point where the user notices.
  5. Starvation. Continuously schedule high-priority updates; watch low-priority work never complete. Then design the escalation rule. (Compare Chromium's anti-starvation, bi-11.)
  6. Tearing. Read a mutable external value at two points in one interruptible render, mutating it in between. Observe inconsistent output. This is the problem useSyncExternalStore exists for — meet it before reading about it.