Update loop
Running an application is a small, closed cycle: execute the program while gathering what it emits, reconcile
that onto the page, then wait. A Set schedules the next turn of the cycle. That's all there is to it.
One cycle
Run
Run executes the program body under the run interpreter, seeding state from the
values persisted last time (plus any pending updates).
Gather
As components run they emit node descriptors into a gather stack. The run ends with a tree of descriptors: plain data, not DOM.
Reconcile
The driver matches the new tree to the last one by position and patches only what differs, text or an attribute, reusing the elements that stayed.
Wait
A Set queues its update and schedules a flush on a microtask. The flush re-runs each mount and
reconciles. Back to step 1.
The shape in code
// one cycle: run the body (gathering), return the new tree + persisted state
function runCycle(program, updates) {
return Reactive._inMode("run", () => {
readValues = { ...program.values, ...updates }; // last run's state + edits
liveValues = {}; idCounter = 0; gatherStack = [ { children: [] } ];
program.body(); // emits into the gather
return { program: { …, values: liveValues }, tree: gatherStack[0].children };
});
}
Mount runs the first cycle and renders the tree; every later cycle comes from
flush, which reconciles rather than rebuilding. State persists because each State reads
readValues by its stable id and writes liveValues,
which becomes the next program's memory; any state left untouched this run simply falls out.
One subtlety: events run in run
A cycle restores the previous interpreter when it finishes, so by the time a user clicks, the current
interpreter is whatever was left installed, which may be something other than run. A click handler
that calls Set would then dispatch to the wrong interpreter. So the driver wraps every event
handler to execute under run: interaction belongs to execution, and that's where
Set and Value live.