This content originally appeared on DEV Community and was authored by Shafiq ur rehman
A JavaScript engine is a program that converts JavaScript code into a Binary Language. Computers understand the Binary Language. Every web browser contains a JavaScript engine. For example, V8 is the JavaScript engine in Google Chrome.
Let's dive in!
- Execution context: Execution Context is the environment in which JS code runs. It decides what variables and functions are accessible, and how the code executes. It has two types (Global & Function) and works in two phases (Memory Creation & Code Execution).
1. Global Execution Context (GEC)
This is created once when your script starts. It's the outermost context where:
- Global variables and functions are stored
-
this
refers to the global object (likewindow
in browsers)
2. Function Execution Context (FEC)
When you call a function, a new context is created specifically for that function. It manages:
- The function's local variables
- The value of
this
inside the function - Arguments passed to the function
3. Memory Creation Phase
This is the first phase of an execution context. During this phase:
- All variables and functions are allocated in memory
- Functions are fully hoisted (stored with their complete code)
- Variables declared with
var
are hoisted and initialized withundefined
- Variables declared with
let
andconst
are also hoisted but remain uninitialized, staying in the Temporal Dead Zone (TDZ) until their declaration is reached
4. Code Execution Phase
This is the second phase, where:
- The code executes line by line
- Variables receive their actual values
- Functions are called when invoked
The Variable Environment is a part of the Execution Context.
It is where all variables, functions, and arguments are stored in memory as key-value pairs during the Memory Creation Phase.
-
It includes:
- Variable declarations
- Function declarations
- Function parameters
It is used internally by the JS engine to track what's defined in the current scope.
Call stack: The call stack is a part of the JavaScript engine that helps keep track of function calls. When a function is invoked, it is pushed to the call stack, where its execution begins. When the execution is complete, the function is popped off the call stack. It utilizes the concept of stacks in data structures, following the Last-In-First-Out (LIFO) principle.
Event loop: The event loop runs indefinitely and connects the call stack, the microtask queue, and the callback queue. The event loop moves asynchronous tasks from the microtask queue and the callback queue to the call stack whenever the call stack is empty.
In JavaScript’s event loop, microtasks always have higher priority than macrotasks (callback queue).
Callback Queue (Macrotask Queue): Callback functions for setTimeout() are added to the callback queue before they are moved to the call stack for execution.
Includes:
setTimeout()
setInterval()
setImmediate()
I/O events
Microtask queue: Asynchronous callback functions for promises and mutation observers are queued in the microtask queue before they are moved to the call stack for execution.
-
Includes things like:
Promise.then()
Promise.catch()
Promise.finally()
MutationObserver
Synchronous JavaScript
JavaScript is synchronous, blocking, and single-threaded. This means the JavaScript engine executes code sequentially—one line at a time from top to bottom—in the exact order of the statements.
Consider a scenario with three console.log
statements.
console.log("First line");
console.log("Second line");
console.log("Third line");
Output:
First line
Second line
Third line
Let's examine another example:
function getName(name) {
return name;
}
function greetUser() {
const userName = getName("Shafiq Ur Rehman");
console.log(`Hello, ${userName}!`);
}
greetUser();
- A new global execution context is created and pushed onto the call stack. This is the main execution context where the top-level code runs. Every program has only one global execution context, and it always stays at the bottom of the call stack.
- In the global execution context, the memory creation phase starts. In this phase, all variables and functions declared in the program are allocated space in memory (called the variable environment). Since we don’t have variables declared in the global scope, only the functions will be stored in memory.
- The function
getName
is stored in memory, with its reference pointing to the full function body. The code inside it isn’t executed yet—it will run only when the function is called. - Similarly, the function
greetUser
is stored in memory, with its reference pointing to its entire function body. - When the
greetUser
function is invoked, the code execution phase of the global execution context begins. A new execution context forgreetUser
is created and pushed on top of the call stack. Just like any execution context, it first goes through the memory allocation phase. - Inside
greetUser
, the variableuserName
is allocated space in memory and initialized withundefined
. (Note: During memory creation, variables declared withvar
are initialized withundefined
, while variables declared withlet
andconst
are set as uninitialized, which leads to a reference error if accessed before assignment.) - After the memory phase finishes, the code execution phase starts. The variable
userName
needs the result of thegetName
function call. SogetName
is invoked, and a new execution context forgetName
is pushed onto the call stack. -
The function
getName
allocates space for its parametername
, initializes it withundefined
, and then assigns it the value"Shafiq Ur Rehman"
. Once thereturn
statement runs, that value is returned to thegreetUser
context. ThegetName
execution context is then popped off the call stack. Execution goes back togreetUser
, where the returned value is assigned touserName
. Next, theconsole.log
statement runs and prints:
Hello, Shafiq Ur Rehman!
Once done, the
greetUser
The execution context is also popped off the call stack. Finally, the program returns to the global execution context. Since there’s no more code left to run, the global context is popped off the call stack, and the program ends.
Asynchronous JavaScript
Unlike synchronous operations, asynchronous operations don't block subsequent tasks from starting, even if the current task isn't finished. The JavaScript engine works with Web APIs (like setTimeout, setInterval, etc.) in the browser to enable asynchronous behavior.
Using Web APIs, JavaScript offloads time-consuming tasks to the browser while continuing to execute synchronous operations. This asynchronous approach allows tasks that take time (like database access or file operations) to run in the background without blocking the execution of subsequent code.
Let’s break this down with a setTimeout()
example. (I’ll skip memory allocation here since we already covered it earlier.)
console.log("first");
setTimeout(() => {
console.log("second");
}, 3000);
console.log("third");
Here’s what happens when this code runs:
- The program starts with a global execution context created and pushed onto the call stack.
- The first line
console.log("first")
runs. It creates an execution context, prints"first"
to the console, and then is popped off the stack. - Next, the
setTimeout()
function is called. Since it’s a Web API provided by the browser, it doesn’t run fully inside the call stack. Instead, it takes two arguments: a callback function and a delay (3000ms here). The browser registers the callback function in the Web API environment, starts a timer for 3 seconds, and thensetTimeout()
itself is popped off the stack. - Execution moves on to
console.log("third")
. This prints"third"
immediately, and that context is also popped off. - Meanwhile, the callback function from
setTimeout
is sitting in the Web API environment, waiting for the 3-second timer to finish. - Once the timer completes, the callback doesn’t go straight to the call stack. Instead, it’s placed into the callback queue. This queue only runs when the call stack is completely clear. So even if you had thousands of lines of synchronous code after
setTimeout
, they would all finish first. - The event loop is the mechanism that keeps watching the call stack and the queues. When the call stack is empty, the event loop takes the callback from the queue and pushes it onto the stack.
- Finally, the callback runs:
console.log("second")
prints"second"
to the console. After that, the callback function itself is popped off, and eventually, the global execution context is cleared once everything has finished.
Conclusion
JavaScript runs code synchronously but can handle async tasks using browser Web APIs. Knowing how the engine works under the hood is key to mastering the language.
“Let me know your thoughts in the comments,” or “Follow me for more JavaScript insights.”
This content originally appeared on DEV Community and was authored by Shafiq ur rehman

Shafiq ur rehman | Sciencx (2025-08-21T10:19:34+00:00) JavaScript Execution Context Made Simple. Retrieved from https://www.scien.cx/2025/08/21/javascript-execution-context-made-simple/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.