35 min read Runtime Architecture Lesson 6 (of 12)
Part 2 — Browser Engine, JavaScript & TypeScript

Lesson 06 — JavaScript Runtime: How Code Actually Executes

Demystifying the JavaScript Engine, Call Stack, Memory Heap, Execution Contexts, Web APIs, the Event Loop, Microtasks, and why JavaScript excels as an asynchronous single-threaded powerhouse.

What We Will Learn in this Lesson

By the end of this lesson, you will clearly understand:

Where JavaScript runs inside the browser
What a JavaScript Engine is and how it processes code
The roles of the Call Stack and the Memory Heap
Execution Contexts and Scope chain mechanics
Synchronous vs Asynchronous execution
How Web APIs supercharge JavaScript capabilities
The connection between Callbacks, Promises, and async/await
How the Event Loop coordinates tasks and microtasks
Why JavaScript is single-threaded and what Web Workers do
How this complete runtime powers modern web applications

1. What Happens When a JavaScript File Reaches the Browser?

Suppose an HTML document includes an external script:

<script src="app.js"></script>

The browser fetches app.js over the network. Inside the file lies a single instruction:

console.log("Hello BitBotLab");

The browser does not execute this file as naive line-by-line raw text.

To ingest, parse, compile, and run JavaScript code, every modern browser embeds a dedicated execution subsystem known as the JavaScript Engine.

Google Chrome / Edge / Node.js V8 Engine
Mozilla Firefox SpiderMonkey
Apple Safari JavaScriptCore (Nitro)

2. What is a JavaScript Engine?

A JavaScript Engine is a specialized software program that converts human-readable JavaScript source code into native machine code instructions that the host CPU can execute.

Conceptually, the internal execution pipeline looks like this:

JavaScript Source Code (Text)
↓
Parser (Tokenizer → AST - Abstract Syntax Tree)
↓
Bytecode Generator (Interpreter)
↓
JIT Compiler (Just-In-Time Optimization)
↓
Optimized Machine Code → CPU Execution

Modern engines are not simple interpreters; they utilize Just-In-Time (JIT) Compilers. Code paths that run repeatedly ("hot code") are profiled on the fly and compiled into highly optimized binary machine instructions directly in memory.

3. What Does the Engine Actually Do?

The core responsibility of the engine can be stated simply:

"Translate a high-level JavaScript program into machine-executable operations and run them efficiently on hardware."

Consider this standard code fragment:

let a = 10;
let b = 20;

let result = a + b;

console.log(result);

The engine coordinates multiple runtime components to:

  • Allocate memory registers for variables a and b
  • Store the literal numbers 10 and 20
  • Instruct the arithmetic logic unit (ALU) to perform binary addition
  • Store the sum in the memory allocated for result
  • Invoke the console.log() host method to output data to the developer console

4. Where Does Memory Live? (The Memory Heap)

Whenever a program executes, data, objects, arrays, and functions must be stored somewhere in RAM.

In the JavaScript runtime, the region dedicated to dynamic memory allocation is known as the Memory Heap.

const user = {
  name: "Rahim",
  age: 25
};

Here, the substantial, unstructured data of the user object resides in the Memory Heap, while the variable identifier holds a memory reference (pointer) to that heap address.

The Memory Heap is a vast, unorganized memory pool where complex objects, arrays, closures, and function definitions are allocated and periodically reclaimed by the Garbage Collector.

5. What is the Call Stack?

Next comes the most crucial execution coordinator in JavaScript: the Call Stack.

As instructions execute, the runtime must continually track its location in the code:

"Which function is currently executing? Where must control return once this function finishes?"

The runtime organizes this using a fundamental data structure: the Call Stack.

function greet() {
  console.log("Hello");
}

greet();
Call Stack State
greet() [Running]
Global Execution Context

When greet() returns, the engine pops its stack frame off the top of the Call Stack.

6. Why Do We Need a Stack? (LIFO)

The elegance of a stack becomes apparent when observing nested function invocations:

function first() {
  second();
}

function second() {
  third();
}

function third() {
  console.log("Done");
}

first();

The sequence of stack frames:

third() ← Top of Stack
second()
first()
Global Context ← Base of Stack

Notice that third() was added last, yet finishes and exits first. This classic behavior is called:

LIFO — Last In, First Out

7. What is an Execution Context?

Whenever JavaScript executes code, it does so inside an environment known as an Execution Context.

An Execution Context bundles together:

  • Local variables, arguments, and function declarations (Variable Environment)
  • Pointers to parent lexical scopes (Scope Chain)
  • The evaluation of the this keyword

Two primary execution contexts exist within the runtime:

  1. Global Execution Context (GEC): Initialized the moment a script loads. In browser environments, its global object is window.
  2. Function Execution Context (FEC): Spawned uniquely every time a function is invoked, and pushed onto the Call Stack.

8. What is Scope?

Scope defines the accessibility boundaries of variables and functions within your code:

function test() {
  let message = "Hello";
}

test();
console.log(message); // ReferenceError: message is not defined!

Modern JavaScript features three primary scoping tiers:

  • Global Scope: Accessible from any file or function.
  • Function Scope: Confined within the body of a function (variables declared with var).
  • Block Scope: Confined strictly within { } curly brackets (variables declared with let and const).

9. JavaScript is Naturally Synchronous

By default, JavaScript executes in a synchronous, blocking fashion:

console.log("A");
console.log("B");
console.log("C");

The output is guaranteed to be: A → B → C. Each instruction must completely finish before the next line begins.

Now consider a serious real-world dilemma:

The Peril of Synchronous Blocking: If fetching user data from a remote server takes 3 seconds, and JavaScript blocks the thread waiting for that response, the user cannot click buttons, highlight text, or scroll the page during those 3 seconds! The browser freezes completely.

This is why Asynchronous Programming is mandatory for the web.

10. The Browser is Far More Than Just a JavaScript Engine

Remember this essential reality of modern web architecture:

The web browser is not merely a JavaScript engine; the browser is a comprehensive host environment!

A browser environment encompasses:

  • The JavaScript Engine (Call Stack + Memory Heap)
  • The DOM and CSSOM parsers
  • The Rendering Pipeline (Layout, Paint, Compositing)
  • The Networking Stack (HTTP, TCP, TLS, WebSockets)
  • Web APIs (Timers, Geolocation, Storage, Canvas, Fetch)
  • The Task Queues and the Event Loop

11. How Web APIs Assist JavaScript

When you call:

setTimeout(() => {
  console.log("Done");
}, 2000);

The JavaScript engine does not pause execution for 2,000 milliseconds. Instead, it offloads the timer countdown to the browser's Timer Web API and immediately clears the Call Stack!

Similarly, fetch('/api/products') delegates network transport to the browser's multi-threaded background networking subsystem.

12. How `setTimeout()` Really Works

Consider this classic interview puzzle:

console.log("A");

setTimeout(() => {
  console.log("B");
}, 2000);

console.log("C");

What is the output? A → B → C? Not at all!

The real output is:

A → C → B (after 2 seconds)

The callback inside setTimeout is scheduled for future execution. It waits in a queue until all synchronous code has executed and the Call Stack is empty.

13. What is the Event Loop?

The Event Loop is the continuous coordinator between asynchronous browser subsystems and JavaScript execution.

"The sole responsibility of the Event Loop is to continuously monitor the Call Stack. The moment the Call Stack is completely empty, it takes the readiest callback from the Task Queue and pushes it onto the Call Stack."
The Event Loop Flow
Call Stack (Executes Synchronous Code)
↓ Offloads async operations
Web APIs (Timers, Network, DOM Events in Background)
↓ Operation settles, queues callback
Task Queue / Callback Queue
↑ ↓ Event Loop checks: Is Call Stack empty?
Callback moved onto Call Stack → Executes!

14. What is a Callback?

A callback is simply a function passed as an argument into another function, designed to be called once a specific asynchronous operation has finished:

// This callback runs after 1000 milliseconds
setTimeout(() => {
  console.log("Finished");
}, 1000);

15. Why Did Promises Emerge?

When multiple interdependent asynchronous tasks were chained together, deeply nested callback functions formed an unmaintainable tangle known as Callback Hell or the Pyramid of Doom:

getUser(userId, (user) => {
  getOrders(user.id, (orders) => {
    getOrderDetails(orders[0].id, (details) => {
      // Error handling and debugging became an unreadable nightmare!
    });
  });
});

To eliminate this nesting nightmare and standardize asynchronous flows, modern JavaScript introduced Promises.

16. What is a Promise?

A Promise is an object representing the eventual completion (or failure) of an asynchronous operation and its resulting value.

A Promise exists in one of three mutually exclusive states:

Pending Initial state; operation still in progress
Fulfilled The operation succeeded with a resolved value
Rejected The operation failed with an error reason

17. Handling Promises

We attach handlers to a promise using .then() for success and .catch() for errors:

fetch("/api/products")
  .then(response => response.json())
  .then(data => {
    console.log("Products loaded:", data);
  })
  .catch(error => {
    console.error("Request failed:", error);
  });

18. What is `async/await`?

Introduced in ES2017 (ES8), async/await provides cleaner, linear, and more readable syntax on top of Promises:

async function loadProducts() {
  try {
    const response = await fetch("/api/products");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Error:", error);
  }
}

While await reads like synchronous code, it operates non-blockingly using promises under the hood.

19. Does `await` Block the Entire JavaScript Runtime?

No! This is a widespread beginner misconception.

When the engine encounters await, it only pauses execution of that specific async function until the promise settles. The browser's Main Thread, UI rendering, user interaction handlers, and other JavaScript functions remain completely unblocked!

20. Is JavaScript Really Single-Threaded?

Yes: the JavaScript execution engine is strictly Single-Threaded.

This means there is only one Call Stack. At any given instant, the CPU executes exactly one JavaScript instruction.

However, the browser host system is thoroughly Multi-Threaded. Background threads handle network sockets, disk operations, audio streams, and GPU compositing in parallel.

21. What is a Web Worker?

If your application requires heavy CPU-bound computation (e.g., cryptographic hashing, image filtering, physics calculations), the browser provides Web Workers:

Main Thread UI, DOM, User Events, Render Pipeline
↔ postMessage ↔
Web Worker Heavy Computation in Background

22. Why the Main Thread is Sacred

The browser's Main Thread handles three critical, shared tasks:

  • JavaScript execution
  • Dispatching user clicks, inputs, and scrolls
  • Coordinating the display rendering pipeline

If you mistakenly run an infinite or expensive blocking loop on the Main Thread:

while (true) {
  // The main thread is locked forever!
}

The user's browser tab hangs instantly. The golden rule of frontend engineering: Never block the Main Thread!

23. A Real-World Walkthrough: The Search Box Flow

Here is what happens inside the runtime when a user types into a live search box:

User types "computer science" into input box
↓
Browser fires 'input' event → Event Listener callback runs
↓
Debounce timer schedules network request
↓
fetch('/api/search') handed over to Browser Network Thread
↓
Server queries Database → Returns JSON response
↓
Promise resolves → await resumes in JS Engine
↓
DOM nodes updated with search results
↓
Rendering Pipeline paints new search list smoothly

24. A Complete Working Example

Let us analyze an end-to-end production snippet:

const button = document.querySelector("#loadButton");
const output = document.querySelector("#output");

button.addEventListener("click", async () => {
  output.textContent = "Loading...";

  try {
    const response = await fetch("/api/user");
    const user = await response.json();

    output.textContent = `Hello, ${user.name}`;
  } catch (error) {
    output.textContent = "Something went wrong.";
  }
});

Step-by-step runtime breakdown:

  1. DOM Query: References to the button and output nodes are stored.
  2. Event Registration: Click listener is registered with the browser host.
  3. User Click: The event triggers, pushing the callback onto the Call Stack.
  4. Immediate UI Feedback: output.textContent = "Loading..." updates the DOM.
  5. Network Offload: fetch() delegates network I/O, suspending the async function.
  6. Data Arrival: When the promise resolves, microtask resumption restores the function and updates the DOM.

25. Call Stack + Web APIs + Event Loop Architecture

This architectural diagram illustrates how all runtime components interact:

                  BROWSER ENVIRONMENT
                           │
          ┌────────────────┴────────────────┐
          │                                 │
          ▼                                 ▼
   JAVASCRIPT ENGINE                    WEB APIS
          │                                 │
   ┌──────┴──────┐                  ┌───────┼───────┐
   │             │                  │       │       │
   ▼             ▼                Timer   Fetch    DOM
Memory Heap  Call Stack             │       │     Events
                 │                  │       │       │
                 │                  ▼       ▼       ▼
                 │            CALLBACK / TASK QUEUE
                 │                          │
                 └──────────────┬───────────┘
                                ▼
                           EVENT LOOP
                                │
                                ▼
                       Back to Call Stack

26. Understanding the Microtask Queue

The browser manages two task queues with strict prioritization:

  • Macrotask Queue (Task Queue): setTimeout, setInterval, I/O events.
  • Microtask Queue (Higher Priority): Promise resolutions (.then, await), queueMicrotask.

The golden rule of the Event Loop: After every task finishes, and before the browser renders the next visual frame, the entire Microtask Queue must be completely drained!

console.log("A");

Promise.resolve().then(() => {
  console.log("B");
});

console.log("C");

The output is: A → C → B. Because B was queued as a microtask, it ran immediately after the synchronous script finished, before any macrotasks or repaint events.

27. Why Runtime Knowledge Matters for Frontend Engineers

Understanding runtime mechanics separates junior developers from senior software architects:

  • Diagnosing interaction latency on buttons and forms
  • Understanding why the UI updates after an API response
  • Preventing UI freezes and jank caused by heavy CPU loops
  • Properly sequencing parallel vs sequential await operations

28. The Complete Journey: Rendering Pipeline Meets JavaScript Runtime

Observe how the two halves of web development unite:

1. Browser Rendering Pipeline

HTML → DOM
CSS → CSSOM
Render Tree
Layout (Reflow)
Paint → Rasterize → Composite → Pixels

2. JavaScript Runtime Execution

JS Engine (V8)
Call Stack + Heap
Web APIs (Timers, Network)
Task / Microtask Queues
Event Loop → State Update → Re-render

29. Core Takeaways

JavaScript Engine Compiles and runs JS code (V8, SpiderMonkey)
Call Stack Tracks currently executing functions in LIFO order
Memory Heap Allocates memory for objects and dynamic data
Web APIs Browser background host features (Timer, Fetch, DOM Events)
Event Loop Pushes queued tasks onto the Call Stack when empty
Single-Threaded One stack, but backed by a multi-threaded host browser

Knowledge Check — JavaScript Runtime & Event Loop

Test your understanding with these three core conceptual questions:

1. When setTimeout() is invoked, what subsystem actually counts down the time?

2. When does the Event Loop move a callback from the Task Queue onto the Call Stack?

3. Where do Promise .then() callbacks queue, and what is their execution priority?

What's Coming Next?

We now understand HTML, CSS, JavaScript, and both browser rendering and runtime execution models. However, building massive applications with hundreds of interactive UI elements using raw JavaScript quickly devolves into spaghetti code. How do modern development teams keep frontend architectures modular, reactive, and maintainable? In the next lesson, we explore: From JavaScript to Modern Frontend Frameworks: Why React, Vue & Angular Became Essential!