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

1

Run

Run executes the program body under the run interpreter, seeding state from the values persisted last time (plus any pending updates).

2

Gather

As components run they emit node descriptors into a gather stack. The run ends with a tree of descriptors: plain data, not DOM.

3

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.

4

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 };
  });
}
One cycle of the update loop in code

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.

This is exactly the bug the live counter hit first: clicks silently did nothing until handlers were wrapped in _inMode("run", …). It's in the source.