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.
- Your
fw-02renderer is recursive. Render a tree of 10,000 nodes. Measure the task length. Compare to the frame budget frombi-10. Observe the dropped frames directly. - 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.
- Therefore: make the stack an explicit data structure. A node with
child,sibling,returnpointers, walked with a loop instead of recursion. Now "where am I" is a value you can hold. - Now yielding is possible. Loop while work remains and time remains; otherwise schedule
a continuation (
bi-11: which primitive, and why not microtasks?). - 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.
- Now you need double buffering. Building the new tree while the current one is displayed means two trees and a pointer swap — an alternate.
- 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.
- Batching falls out. Multiple
setStatecalls 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
- Commit interruption. Deliberately yield in the middle of the commit phase. Produce a visibly half-updated UI. This is why commit is atomic.
- 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."
- Yield too often. Measure total time as you shrink the yield interval. Find the point where scheduling overhead dominates.
- Yield too rarely. Measure input delay. Find the point where the user notices.
- Starvation. Continuously schedule high-priority updates; watch low-priority work never
complete. Then design the escalation rule. (Compare Chromium's anti-starvation,
bi-11.) - Tearing. Read a mutable external value at two points in one interruptible render, mutating
it in between. Observe inconsistent output. This is the problem
useSyncExternalStoreexists for — meet it before reading about it.