JavaScript Runtime Architecture
18.1 JavaScript Runtime Architecture
Section titled “18.1 JavaScript Runtime Architecture”The JavaScript Runtime Environment is what allows JavaScript code to run. While JavaScript itself is a single-threaded language, the runtime environment provides the necessary components to handle asynchronous operations, I/O, and other tasks efficiently.
What is JavaScript Runtime?
Section titled “What is JavaScript Runtime?”JavaScript was originally designed to run only in browsers. Today, JavaScript runs in various environments (browsers, Node.js, Deno, Bun), each providing a runtime that includes:
- JavaScript Engine (V8, SpiderMonkey, JavaScriptCore) - Executes JS code
- Web APIs / C++ APIs - Environment-specific features
- Event Loop - Orchestrates asynchronous operations
- Callback Queues - Hold pending callbacks
- Microtask Queue - High-priority async callbacks
- Render Queue - Browser visual updates
Visual Architecture Diagram
Section titled “Visual Architecture Diagram”flowchart TD Start([Start]) --> Stack
subgraph Stack["Call Stack"] direction TB S1["Function Frame 3 (top)"] S2["Function Frame 2"] S3["Function Frame 1"] S4["Global Context"] end
subgraph Heap["Heap"] H1["Objects"] H2["Functions"] H3["Closures"] H4["Arrays"] end
subgraph WebAPIs["Web APIs / C++ APIs"] W1["setTimeout / setInterval"] W2["fetch / XHR"] W3["DOM Events"] W4["fs / crypto (Node.js)"] end
subgraph Queues["Task Queues"] direction LR
subgraph MQ["Microtask Queue (Priority 1)"] M1["Promise.then"] M2["queueMicrotask"] M3["MutationObserver"] end
subgraph CQ["Callback Queue (Priority 2)"] C1["setTimeout Callbacks"] C2["I/O Callbacks"] C3["Event Handlers"] end
subgraph RQ["Render Queue (Priority 3)"] R1["Repaint"] R2["Reflow"] R3["requestAnimationFrame"] end end
EL["Event Loop"] --> Stack
Stack -->|"API Call"| WebAPIs WebAPIs -->|"Callback"| CQ Stack -->|"Promise"| MQ MQ -->|"Execute All"| EL CQ -->|"Execute One"| EL RQ -->|"Execute"| EL
Stack -.->|"Reference"| Heap
style MQ fill:#90EE90,stroke:#006400,stroke-width:2px style CQ fill:#FFD700,stroke:#8B8000,stroke-width:2px style RQ fill:#87CEEB,stroke:#00008B,stroke-width:2px style EL fill:#FF6B6B,stroke:#8B0000,stroke-width:3pxCore Components Detailed
Section titled “Core Components Detailed”1. Call Stack
Section titled “1. Call Stack”A LIFO data structure that tracks function execution. Each function call creates a stack frame containing arguments, local variables, and return address.
2. Heap
Section titled “2. Heap”Unstructured memory region where objects, functions, closures, and dynamically allocated data are stored. Managed by garbage collection.
3. Web APIs / C++ APIs
Section titled “3. Web APIs / C++ APIs”External features provided by the environment:
- Browser: DOM, fetch, setTimeout, IndexedDB, WebSockets
- Node.js: fs, crypto, http, child_process, dns
4. Callback Queue (Macrotask Queue)
Section titled “4. Callback Queue (Macrotask Queue)”FIFO queue holding callbacks from setTimeout, setInterval, I/O operations, and event listeners.
5. Microtask Queue
Section titled “5. Microtask Queue”Higher priority queue for Promise callbacks, queueMicrotask, and MutationObserver. Processed completely before any macrotask.
6. Render Queue
Section titled “6. Render Queue”Browser-specific queue handling repaints, reflows, and animation frames. Processed between macrotasks.
7. Event Loop
Section titled “7. Event Loop”Continuous process monitoring the call stack and queues, moving callbacks to the stack when empty.
How Components Work Together
Section titled “How Components Work Together”// Complete example demonstrating all runtime componentsconsole.log("1: Start - Call Stack: [global]");
// Web API: setTimeout - Goes to Web API, then Callback QueuesetTimeout(() => { console.log("7: setTimeout - From Callback Queue (macrotask)");}, 0);
// Web API: fetch - Network requestfetch("https://jsonplaceholder.typicode.com/posts/1") .then((response) => response.json()) .then((data) => { console.log("6: fetch completed - From Microtask Queue"); });
// Microtask: PromisePromise.resolve() .then(() => { console.log("3: Promise 1 - From Microtask Queue"); }) .then(() => { console.log("5: Promise 2 - From Microtask Queue"); });
// Microtask: queueMicrotaskqueueMicrotask(() => { console.log("4: queueMicrotask - From Microtask Queue");});
// Synchronous codeconsole.log("2: End - Call Stack: [global]");
/* Execution Order:1: Start2: End3: Promise 14: queueMicrotask5: Promise 26: fetch completed7: setTimeout
Explanation:- All microtasks execute before any macrotask- fetch Promise resolves as microtask- setTimeout callback is macrotask*/Runtime Environment Comparison
Section titled “Runtime Environment Comparison”Browser JavaScript Runtime
Section titled “Browser JavaScript Runtime”// Browser-specific Web APIsconst element = document.createElement("div");element.textContent = "Hello Browser!";document.body.appendChild(element);
// Fetch API for network requestsfetch("https://api.example.com/data") .then((response) => response.json()) .then((data) => console.log(data));
// Timer APIsconst timeoutId = setTimeout(() => { console.log("Delayed execution");}, 1000);
const intervalId = setInterval(() => { console.log("Repeated execution");}, 2000);
// Animation frame (syncs with monitor refresh)requestAnimationFrame((timestamp) => { console.log("Frame at:", timestamp);});
// DOM eventsbutton.addEventListener("click", () => { console.log("Click event - macrotask");});
// Storage APIslocalStorage.setItem("key", "value");sessionStorage.setItem("temp", "data");
// IndexedDB for large dataconst request = indexedDB.open("MyDB", 1);Node.js JavaScript Runtime
Section titled “Node.js JavaScript Runtime”const fs = require("fs");const crypto = require("crypto");const http = require("http");const { spawn } = require("child_process");
// File system API (non-blocking I/O)fs.readFile("/path/to/file.txt", "utf8", (err, data) => { if (err) throw err; console.log("File read complete:", data);});
// Crypto API (uses thread pool)crypto.pbkdf2("password", "salt", 100000, 64, "sha512", (err, derivedKey) => { console.log("Hash generated:", derivedKey.toString("hex"));});
// HTTP Serverconst server = http.createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello Node.js\n");});server.listen(3000);
// Child Processconst ls = spawn("ls", ["-la"]);ls.stdout.on("data", (data) => { console.log(`Output: ${data}`);});
// setImmediate (Node.js macrotask)setImmediate(() => { console.log("setImmediate callback");});
// process.nextTick (microtask, higher priority than Promise)process.nextTick(() => { console.log("nextTick - Highest priority microtask");});Memory and Performance Implications
Section titled “Memory and Performance Implications”// Understanding runtime memory management
// 1. Stack memory (fast, limited)function stackExample() { let localVar = 42; // Stored on stack const primitive = "hello"; // Stored on stack // Stack frame cleaned up when function returns}
// 2. Heap memory (slower, large)function heapExample() { const object = { name: "JavaScript" }; // Stored on heap const array = new Array(1000000); // Stored on heap // Reference stored on stack, object in heap}
// 3. Memory leak examplelet globalCache = new Map();function memoryLeak() { const largeData = new Array(1000000).fill("data"); globalCache.set(Date.now(), largeData); // Never cleared - LEAK}
// 4. Proper cleanupfunction noMemoryLeak() { const largeData = new Array(1000000).fill("data"); processData(largeData); // No external reference - eligible for GC}
// 5. Monitoring memory usageif (performance.memory) { setInterval(() => { console.log({ totalHeap: (performance.memory.totalJSHeapSize / 1048576).toFixed(2) + " MB", usedHeap: (performance.memory.usedJSHeapSize / 1048576).toFixed(2) + " MB", limit: (performance.memory.jsHeapSizeLimit / 1048576).toFixed(2) + " MB", }); }, 5000);}
// 6. WeakRef for non-critical cachingconst cache = new WeakMap();let data = { id: 1, value: "important" };cache.set(data, "metadata");data = null; // Both can be garbage collectedEvent Loop Priority Summary
Section titled “Event Loop Priority Summary”PRIORITY ORDER (Highest to Lowest):
1. process.nextTick() [Node.js only]2. Promise callbacks3. queueMicrotask()4. MutationObserver5. I/O callbacks (fs, network)6. setTimeout / setInterval7. setImmediate [Node.js]8. requestAnimationFrame [Browser]9. Render events [Browser]10. Other macrotasks (DOM events, etc.)Best Practices
Section titled “Best Practices”// 1. Don't block the event loop// Bad - blocks everythingfunction badLongOperation() { const start = Date.now(); while (Date.now() - start < 5000) { // Blocks for 5 seconds }}
// Good - non-blockingfunction goodLongOperation() { setTimeout(() => { performChunkedWork(); }, 0);}
// 2. Use appropriate queues// High priority async - use microtasksPromise.resolve().then(() => { updateCriticalUI();});
// Low priority - use macrotaskssetTimeout(() => { logAnalytics();}, 0);
// 3. Avoid deep recursion (can overflow stack)// Badfunction badRecursion(n) { if (n <= 0) return; badRecursion(n - 1);}
// Goodfunction goodIteration(n) { for (let i = 0; i < n; i++) { // Process }}
// 4. Clear references for GClet largeObject = { data: new Array(1000000) };// After use:largeObject = null; // Eligible for garbage collection
// 5. Use Web Workers for CPU-intensive tasksconst worker = new Worker("heavy-task.js");worker.postMessage({ data: largeDataSet });worker.onmessage = (event) => { console.log("Result:", event.data);};