Glossary / Run-to-completion

Run-to-completion

Run-to-completion means a JavaScript job runs until its execution stack is empty before another job on the same agent begins. Another task does not interrupt a synchronous function halfway through and interleave its statements. This simplifies reasoning about many state changes, but it does not eliminate race conditions across asynchronous boundaries or shared memory between workers.

Consequences

let count = 0;
function incrementAndRead() {
  count++;
  console.log(count);
}

Within one ordinary synchronous task, another event callback on that agent cannot run between count++ and console.log. But if the function awaits, yields to a callback, or schedules work, other jobs can run before the continuation, and shared state may change. Reentrancy is another exception to naive assumptions: a synchronous function invoked by a getter, proxy trap, event dispatch, or user callback can call back into the current subsystem before its outer call returns.

Run-to-completion applies per agent; Web Workers and Node worker threads have separate agents. If agents share a SharedArrayBuffer, their memory operations can interleave and require Atomics or another synchronization protocol. A long job monopolizes its agent, harming browser responsiveness or Node throughput. Break CPU work into cooperative chunks or move it to a worker.

Interview questions

  1. Can a browser event handler interrupt a synchronous loop? Not on the same agent; it waits until the current job completes.
  2. Does run-to-completion prevent race conditions? No; async continuations and separate agents can observe changing shared state.
  3. What is reentrancy? A callback re-enters a component before the outer operation finishes.
  4. How make a long task responsive? Chunk and yield, or move CPU work to another worker.

References