UNDER THE HOODExecution, made visible.
01 / 12 · THE ENGINE

Before the first line

Bindings, hoisting & execution contexts

0 / 0
Drag to revisit any state
</>

Source code

browser JavaScript
Highlighted line = action just taken. Built-in frames are collapsed.
What just happened
0

Before we run

How to infer it

Before stepping: predict the output

Enter logs in order, separated by commas or new lines.

Call stack

Browser timers

sim 0 ms
Ready to beginPress Next step to prepare the script.
Setup
µ

Microtasks

0
FRONT ← oldest ready job · new jobs append →

Ready timer tasks

0
FRONT ← next from this timer source
›_

Console

0 logs

Bindings & retained state

Lesson complete. Replay the tricky transition, then try the next one.
A guided, deterministic teaching trace — not a live debugger for arbitrary code. Timings are simulated; examples target browser main-thread JavaScript with native Promises. Extra engine bookkeeping is omitted.
Start with this mental model

One place runs JS. Several places wait.

Think of the call stack as the workbench. A function can run there now, or its callback can wait elsewhere. The event loop coordinates when queued work is allowed to return to that workbench. Memory and scope tell the running code what its variables mean.

01 / SOURCE → BINDINGS

Prepare names. Then evaluate statements.

Parsing and declaration setup establish the bindings that code will use. In our example, a var is initialized to undefined, while let/const remain uninitialized until their declaration executes. This is a semantic model, not a physical “move code upward” operation.

score → undefined
bonus → TDZ
add → function
02 / THE JS ENGINE

The top frame runs.

An ordinary call pushes a context; a return removes it. The caller waits below. Environments hold bindings; objects and function references can remain reachable after a particular call finishes. A closure keeps access to its outer bindings, not an immortal running frame.

second() ← running
first()  ← waiting for return
script   ← waiting for return
03 / THE BROWSER HOST

A wait is not a running callback.

The browser manages timer waits. When a timer is eligible, its callback can become a runnable task. It still cannot interrupt the current ordinary JS execution. Networking is host-managed too, although these labs use timers so that the examples are deterministic.

register → waiting → ready task
ready task ≠ currently executing
04 / MICROTASKS

Ready Promise reactions go here.

A pending Promise retains its registered reactions. When it settles, the appropriate reaction jobs become ready. queueMicrotask directly queues a callback. An await continuation becomes runnable when the awaited value is ready; the rest of JS is not frozen.

pending → reaction is waiting
settled → reaction job can be queued
For the ordinary browser examples in these labs
Run current JSCalls and returns use the stack.
Drain microtasksIncluding new jobs added during the drain.
Possible renderingOnly at an available opportunity.
Select another taskThen another checkpoint. Repeat.

This is a useful overview, not the browser’s complete scheduling specification. Browsers have multiple task queues and additional checkpoint locations. The animated task panel shows one timer source. Rendering is not guaranteed after each task.

Ask these three questions at every step:
What code is running now? What work has become ready? Which scheduling rule allows that ready work to run next?
Precision without the confusion

The nuances worth remembering.

Use the animations to infer the example. Use these boundaries to avoid turning a helpful model into an incorrect universal rule.

Is a Promise “asynchronous work in another thread”?

No. A Promise represents an eventual result. Its constructor invokes the executor synchronously. .then handlers run through scheduled reaction jobs. The host operation producing the result may involve work outside this JS execution, but the Promise itself is not a worker.

Does every .then immediately enter Microtasks?

No. A reaction on a pending Promise is retained until settlement. A reaction on a fulfilled/rejected Promise can be queued. In a chain, later reactions depend on the preceding Promise, so they are not all queued together. Use lessons 7 and 10 to see this.

Do microtasks always beat timers?

Already queued microtasks drain at the checkpoint before the next task. A Promise still waiting for a timer has no ready continuation yet. The timer may have to run first to fulfill it. “All promises first” is the wrong rule.

Is there one universal macrotask queue?

No. HTML specifies multiple task queues and task sources. Selection between queues is host-defined. “Macrotask” is common informal terminology; the HTML standard calls these tasks. We intentionally visualize one timer source.

Does an empty call stack always select another task?

Not by itself. At a microtask checkpoint, the queue is drained, including jobs added during that drain. The browser also performs checkpoints in other specified situations, and may have rendering work. Our traces focus on ordinary script, timer, and Promise examples.

Does setTimeout(fn, 0) execute immediately?

No. A zero requested delay does not let a timer callback preempt current code. Timer nesting rules, throttling, and host scheduling may add delay. The clock in this app is simulated and does not predict elapsed wall-clock time.

Does await Promise.resolve() give the browser a paint?

Not necessarily. It suspends the async function and resumes via microtask handling; it is not a guaranteed yield to a new rendering opportunity. A chain of microtasks can still delay tasks and rendering. See lesson 11.

Are all DOM event handlers asynchronous tasks?

No. User input is integrated with browser task processing, but programmatic dispatchEvent() invokes listeners synchronously. A callback’s API and invocation context matter. Event dispatch is beyond the animated examples here.

Are call-stack frames the same as scope?

No. A caller is not automatically a lexical parent. A function’s variable lookup follows its environment links. When a function returns, a closure may keep bindings reachable even though the original execution frame is gone.

Does resolve mean fulfilled, and does it stop execution?

Calling resolve with a plain value fulfills the Promise. Resolving to another Promise can instead lock in adoption of that Promise’s still-pending state. In neither case does resolve act like a return statement. Executor code continues.

What about Node.js, Web Workers, and rendering callbacks?

The labs target browser main-thread JavaScript with native Promises. Node has host-specific phases and APIs such as process.nextTick. Workers have separate agents. requestAnimationFrame participates in the rendering process; it is not just another timer in this diagram.

What exactly is simulated here?

These are pre-authored, reversible teaching snapshots — not an interpreter or a live debugger. Microtask labels abbreviate jobs. Brief built-in frames, unused result Promises, and engine internals are omitted. A state is shown after the highlighted action. Source outputs are checked separately with real browser JavaScript.

Translate the vocabulary

Execution context

The specification’s model for tracking a running or suspended evaluation, including environment links.

Binding / environment

A name-to-value association, and the structure used to find those associations through lexical scope.

Task

A browser-scheduled unit of work. Our timer task invokes a callback that can execute JavaScript.

Microtask checkpoint

A specified point at which ready microtasks are processed until the queue is empty.

Reaction job

Scheduled work that handles Promise fulfillment/rejection and may invoke an attached handler.

Continuation

The saved “where to resume” part of a suspended evaluation. An await does not restart its function.

Primary-source references

Original examples and guided reasoning, grounded in ECMAScript and the WHATWG HTML/DOM standards. Node.js is cited only to mark the boundary of this browser model. Verified for this guide on 7 September 2026. Reference links require internet; the entire lab works offline.

A practical inference method:
Evaluate the synchronous call path. Record which callbacks are merely registered versus actually queued. When the current execution yields to a checkpoint, drain the microtasks in enqueue order. Then consider the next eligible task and any rendering opportunity — and repeat.

How this panel works