After a framework has worked out what changed, what does the commit pass do, and why does it run uninterrupted?
answer
- apply, not decide
- one pass, nothing half-applied
- mutate, then references, then callbacks
- deferred effects come after paint
basics
~20 sCommit is the apply step. It writes the already-computed changes into the host tree — creating, updating, moving and removing nodes, then wiring references and listeners — as one pass, so nobody ever sees a half-applied screen.
solid answer
~40 sAn update has two halves. The first half is pure bookkeeping: run or re-evaluate the output-producing code, compare it with what is currently on screen, and collect the work to do. Nothing is visible yet. The second half, the commit, performs that work against the host: insert new nodes, write the attributes and text that differ, move reordered children, remove and tear down what is gone, populate references, attach or detach listeners. The commit is driven to completion in one pass — even in runtimes that can pause and resume the compute half — because a partially applied tree is a screen a user or other code could actually observe, and because two components updated from the same event must land together. Ordered callback tiers hang off the end of it.
go deeper
Remember the two halves: first the framework works out what should change, then the commit writes it to the real nodes. Effects and callbacks come after the write, not before.
Be able to list what the commit does — insert, write changed properties, move, remove and tear down, populate references, wire listeners — and name the ordered tiers that hang off it.
Explain why the pass is atomic: a paused commit is an observable half-updated screen, and callbacks that depend on references or on children being mounted need a single ordering authority.
Frame it as the boundary where a speculative, discardable computation becomes irreversible host state, and argue for keeping expensive work out of the pre-paint window as a budget the whole app shares.
## Two halves of one update Every component framework separates **deciding** from **applying**. The deciding half produces a *description* of the intended output — a tree of node descriptors, or, in a fine-grained runtime, a set of dirty bindings — and compares it with what the runtime believes is currently committed. The result is a list of host operations. None of it touches the screen. The **commit** is the applying half: the moment the runtime stops reasoning about the new output and starts mutating the host. It is where the framework's abstraction finally meets a real tree of nodes. Why the split exists at all: - The compute half is **speculative**. It may be thrown away (a newer update arrives, an error propagates to a boundary, the runtime decides to yield). Throwing away a description costs nothing; un-applying host writes costs a visible flicker. - The compute half can be **chunked** across tasks. The commit cannot, because the intermediate states would be observable. - Ordering guarantees the rest of the framework depends on — references populated before user callbacks run, children mounted before parents' callbacks — only make sense if one authority applies everything in a known order. ## What the commit actually performs - **Creation and insertion** of host nodes for parts of the tree that did not exist, inserted before the correct sibling so document order matches the description. - **Property writes** for nodes that are kept: only the attributes, styles and text whose values differ from the previous description. - **Moves** for children that reordered, which is a reinsertion of an existing node rather than a rebuild. - **Removal and teardown** for nodes and instances that are gone: detach listeners, clear references, run cleanup for the subtree, drop the instance state. - **Reference population**, so code that needs the concrete host node has one to read. - **Listener wiring**, either per node or through a root container, depending on the framework's strategy. - **Queueing the callback tiers** that run at defined points around the write. ## The ordered sub-phases Most runtimes commit in roughly this order: 1. **Pre-removal cleanup** — run teardown for instances about to disappear, while their nodes are still in place. 2. **Mutations** — insert, write, move, remove. 3. **References** — point the reference slots at the nodes that now exist. 4. **Layout-sensitive callbacks** — run synchronously, still inside the commit, before the host has had a chance to present the new frame. 5. **The host paints** the committed tree. 6. **Deferred effects** — the ordinary side-effect tier, run after the frame is on screen. | Step | Who runs it | Can the user see the state before it? | |---|---|---| | Produce a description | the framework's compute half | no — nothing is applied | | Compare against the committed tree | the framework's compute half | no | | Mutate host nodes | the commit | not mid-pass; the pass is atomic to the user | | Layout-sensitive callbacks | the commit, before paint | no | | Deferred effects | after paint | yes — the frame is already visible | ## What must not happen during it - **Long synchronous work.** Everything in the commit sits between the update and the next frame; a slow callback in the pre-paint tier delays the pixels. - **Triggering a fresh synchronous update from inside the pass.** Runtimes tolerate it by re-entering with another pass, but it doubles the work before paint and is a common cause of an update loop. - **Assuming the host is quiet.** Other code, a third-party widget, or the platform's own parser fixes may have changed nodes the framework believes it owns; the commit writes what its own bookkeeping says, not what it re-reads. ## Where frameworks differ The apply-versus-decide split is universal, but its size is not. A runtime that re-runs the output-producing function on every change builds a large work list and commits it in one pass. A runtime with fine-grained dependency tracking wires each dynamic value to the exact node at create time, so an update's `commit` is one or two direct writes with almost no list to walk. A compile-time runtime emits those writes ahead of time, so the `commit` is a generated function. In all three the distinction still holds: something computes the new value, and something else writes it to the host.
- Why can a runtime interrupt the compute half of an update but not the commit?The compute half only builds a description and a work list — abandoning it leaves no trace, so it can be chunked across tasks or thrown away when a newer update arrives. The commit mutates real nodes, so stopping halfway leaves a tree that is part old and part new: visible to the user and readable by any other code on the page.
- Where do a component's side-effect callbacks run relative to the host write?After it. The commit mutates nodes and populates references first, then runs the layout-sensitive tier synchronously before the host paints, and the ordinary deferred tier after the frame is on screen. That is why a reference is reliably populated inside any of those callbacks, and why slow work belongs in the deferred tier.
saying these in an interview costs you the question
- Thinks the commit is where the framework decides what changed
- Says component code re-runs during the commit to produce output
- Expects side-effect callbacks to run before the host nodes are written
- Believes the runtime pauses mid-write and resumes in a later task
- Assumes one component's write lands independently of its siblings