Welcome to your first lesson in the JavaScript Mastery course! Our journey begins with the absolute foundation: understanding how JavaScript code actually runs. This lesson unpacks the JavaScript execution model, focusing on the core components of its runtime environment: the call stack, the heap, and the event loop.
Mastering these concepts is the first and most critical step toward the deep conceptual understanding you're aiming for. It lays the groundwork for everything that follows, from asynchronous programming to debugging and performance optimization. Given your background in computer science, you'll find the underlying data structures like stacks and heaps familiar. Our goal is to see how they are orchestrated within JavaScript's unique, single-threaded environment to create a powerful, non-blocking concurrency model.
The JavaScript Engine: The Heart of Execution
At the center of any JavaScript runtime—be it a browser like Chrome or a server-side environment like Node.js—is the JavaScript Engine. The engine's primary job is to take your code and execute it. The most famous example is Google's V8 engine, which powers both Chrome and Node.js.
Every JS engine has two main components that work together to manage code execution and memory.
All you need to know about client-side JS code execution
This article provides a concise overview of the JavaScript runtime. We'll start by reading the definitions of the two core parts of the JS engine.
First, read the section on the Call Stack to understand its role in tracking function execution. Then, read the section on the Memory Heap to see how memory is managed for different data types.
To summarize and expand on what you've just read:
-
The Call Stack: This is a classic LIFO (Last-In, First-Out) stack data structure. Its one job is to track where we are in the code. When a function is called, a "stack frame" containing information about that function (like its arguments and local variables) is pushed onto the stack. When the function returns, its frame is popped off the stack. JavaScript has a single call stack, which is why it's called a single-threaded language. It can only do one thing at a time.
-
The Memory Heap: This is an unstructured region of memory where all the objects, arrays, and functions in your application are stored. When you create an object, for example
const user = { name: 'Alex' };, theuservariable (a reference or "pointer") is placed on the stack, but the actual object{ name: 'Alex' }is created in the heap.
If your code consists only of synchronous operations, the call stack and heap are all you need to describe how it runs. For instance, consider this simple code:
function subtract(a, b) {
return a - b;
}
function calculate() {
const result = subtract(10, 3);
console.log(result);
}
calculate();
Here's the execution flow on the call stack:
- The
calculate()function is called and pushed onto the stack. - Inside
calculate(), thesubtract()function is called and pushed on top of the stack. subtract()returns a value, and its frame is popped off the stack.- The
console.log()function is called and pushed onto the stack. console.log()completes, and its frame is popped off.calculate()has finished, and its frame is popped off. The stack is now empty.
This is straightforward. But what happens when you introduce tasks that take time, like fetching data from a server or setting a timer? If a long-running task stayed on the call stack, it would "block" the single thread, freezing the entire application. This is where the runtime environment becomes much more interesting.
The Bigger Picture: The Runtime Environment
The JavaScript engine doesn't operate in a vacuum. It's embedded within a larger runtime environment, which provides additional capabilities. The browser is the most common runtime, and it adds a set of APIs that allow JavaScript to interact with the outside world.
These additional components are the key to JavaScript's non-blocking behavior:
- Web APIs: These are functionalities provided by the browser, such as the DOM, AJAX (
fetch), and timers (setTimeout). They are not part of the JavaScript language itself but are powerful tools you can use from your JavaScript code. - Callback Queue (or "Task Queue"): This is a FIFO (First-In, First-Out) queue that holds the callback functions ready to be executed.
- Event Loop: This is the orchestrator. Its job is simple but crucial: it continuously monitors both the call stack and the callback queue.
The diagram below illustrates how these components fit together.

Asynchronicity and the Event Loop in Action
Let's see how these parts work together to handle an asynchronous operation like setTimeout.
- Your code calls
setTimeout(myCallback, 1000). This call is pushed onto the Call Stack. - The
setTimeoutfunction is a Web API. The engine recognizes this and hands off the task of running a 1-second timer to the browser's native timer API. ThesetTimeoutfunction call is then immediately popped from the Call Stack. - Your synchronous JavaScript code continues to execute without interruption. The call stack is free to process other functions.
- In the background, the browser's timer counts down. When the 1 second is up, the browser takes
myCallbackand places it into the Callback Queue. - Here comes the Event Loop. It constantly asks: "Is the Call Stack empty?"
- Once all synchronous code has finished running, the Call Stack becomes empty. The Event Loop sees this, checks the Callback Queue, and finds
myCallback. - The Event Loop takes
myCallbackfrom the queue and pushes it onto the Call Stack, where it is finally executed.
This mechanism is at the heart of JavaScript's concurrency model. It allows a single-threaded language to handle numerous operations concurrently without getting blocked.
The following video is a classic and exceptionally clear explanation of this entire process. It will solidify the mental model we've just built.
What the heck is the event loop anyway? | Philip Roberts | JSConf EU
In "What the heck is the event loop anyway?", Philip Roberts provides a fantastic visualization of the concepts we're discussing.
First, watch the segment on the synchronous call stack to see it in action: the call stack. This reinforces how standard function calls are managed. Next, understand the problem that this model creates with slow operations: blocking and async callbacks. Finally, watch the core explanation where all the pieces come together: the event loop. This part visually traces exactly how setTimeout and AJAX requests work without blocking the main thread.
A common pattern you'll see is setTimeout with a delay of 0. Based on the video, can you reason why the following code logs "Start", "End", and then "Timeout"?
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
console.log('End');
The setTimeout call is handed to the Web API, its callback is immediately placed in the Callback Queue, but the Event Loop must wait for the call stack to be empty—which only happens after console.log('End') has run.
Browser vs. Node.js
Your goal is to master JavaScript in both browser and Node.js environments. The good news is that the fundamental model is the same. Node.js also has a V8 engine, a single call stack, and an event loop. The main difference is the APIs it provides. Instead of Web APIs like the DOM, Node.js provides server-side APIs for things like file system access (fs module) and networking (http module). These are handled by a C++ library called libuv, which manages a thread pool to handle heavy tasks, but the interaction with your JavaScript code via the event loop and callback queue remains conceptually identical.
Inside Node.js: A Deep Dive into V8, libuv, the Event Loop ...
This article details the Node.js runtime. Let's read a short section to see the parallels with the browser environment.
Read the section titled "2. Node.js Architecture: More Than Just V8". Pay attention to how it describes the single-threaded event loop and the role of libuv in handling asynchronous I/O, which is analogous to the browser's Web APIs.
Conclusion
In this lesson, we've established the foundational model of the JavaScript runtime. Understanding how these components cooperate is not just academic; it's essential for writing efficient, bug-free, and scalable applications.
Key Takeaways:
- The JavaScript Engine (e.g., V8) contains the Call Stack for tracking function execution and the Memory Heap for storing objects.
- JavaScript is single-threaded, meaning it has only one call stack and can execute one piece of code at a time.
- The broader Runtime Environment (like a browser or Node.js) provides additional APIs (Web APIs or C++ APIs) that can handle long-running tasks in the background.
- When an asynchronous task completes, its callback function is placed in the Callback Queue.
- The Event Loop is the bridge. It moves callbacks from the queue to the call stack for execution, but only when the call stack is empty.
This model is the "why" behind much of JavaScript's behavior. In our next lesson, we will zoom in on the call stack and explore what happens every time a function is called by examining Execution Context. This will clarify how variables are created and how scope works, building directly on the foundation we've laid today.