How JavaScript Executes Code Step by Step
From parsing to the event loop, here is the full journey of a JavaScript program from source code to output.
How JavaScript Executes Code Step by Step
A JS program goes through several stages before producing output. Here is the full journey.
Step 1: Parsing
The engine reads source text, tokenizes it, and builds an AST. Syntax errors stop execution here.
Step 2: Compilation
The AST is compiled to bytecode (V8's Ignition interpreter). Hot code is later optimized to machine code (V8's TurboFan). This is JIT compilation.
Step 3: Global Execution Context
A GEC is created. Memory is allocated for top-level variables and functions. var variables get undefined, function declarations are stored in full, let/const enter the TDZ.
Step 4: Code Execution
The engine runs the GEC code line-by-line:
- Assigns values to variables.
- Invokes functions, creating a function execution context for each.
- Each function context is pushed onto the call stack and popped on return.
Step 5: Async Operations
When the engine hits setTimeout, fetch, or event listeners, it hands them to Web APIs. The engine continues running synchronous code. When the async operation finishes, the callback is queued.
Step 6: Event Loop
Once the call stack is empty, the event loop moves callbacks from the microtask queue (promises, queueMicrotask) to the stack, then from the macrotask queue (timers, events). This repeats forever.
Step 7: Garbage Collection
Throughout execution, the garbage collector reclaims unreachable heap memory in the background.
Example
console.log("start"); setTimeout(() => console.log("timer"), 0); Promise.resolve().then(() => console.log("promise")); console.log("end");
Output: start, end, promise, timer. The promise goes to the microtask queue (runs first), the timer goes to the macrotask queue (runs second).
The Takeaway
JS execution is: parse, compile, create GEC, run synchronously, hand async work to Web APIs, and let the event loop drain microtasks then macrotasks when the stack is empty. Understanding this order explains callback and promise timing.
Parsing (source to AST), compilation (AST to bytecode and machine code), execution context creation, synchronous code execution, async scheduling via Web APIs, and event loop draining queues.
Synchronous code runs first on the call stack. Then the event loop drains the microtask queue (promises), then the macrotask queue (timers, events). This is why promise callbacks run before setTimeout callbacks.
It hands the timer to a Web API, continues running synchronous code, and when the timer fires the callback is pushed to the macrotask queue. The event loop runs it only after the stack is empty.
Because promise callbacks go to the microtask queue, which the event loop drains completely before touching the macrotask queue where setTimeout callbacks live.
Parsing fails, no AST is built, and the entire script (or module) is not executed. The engine reports a SyntaxError and stops before any code runs.
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

