Suggest an editImprove this articleRefine the answer for “What is the call stack in JavaScript?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Call stack** tracks which function is running in JavaScript, using LIFO: last in, first out. ```javascript function a() { b(); } function b() { console.log('b'); } a(); // stack: [a, b] -> [a] -> [] ``` **Key:** one call stack means only one thing runs at a time.Shown above the full answer for quick recall.Answer (EN)Image**Call stack** is the data structure JavaScript uses to track which function is currently running and where to return when it finishes. ## Theory ### TL;DR - The call stack follows LIFO (Last In, First Out): the last function pushed is the first to pop - Each function call creates a **stack frame** holding local variables, the `this` binding, and a return address - JavaScript has one call stack, so only one thing executes at a time - When the stack is empty, the event loop can push the next task - Infinite recursion without a base case fills the stack and throws `RangeError: Maximum call stack size exceeded` ### How the stack works When JavaScript calls a function, it pushes a frame onto the stack. When that function returns, the frame is popped off. The engine always executes whatever is on top. ```javascript function greet(name) { return `Hello, ${name}`; } function run() { const message = greet('Alice'); // greet pushed, then popped console.log(message); // run still on stack } run(); // Stack: [run] -> [run, greet] -> [run] -> [] ``` After `run()` is called, the stack is `[run]`. When `greet` is invoked inside, it becomes `[run, greet]`. When `greet` returns, back to `[run]`. When `run` finishes, the stack empties. ### Stack frames Each frame is not just a pointer. It holds the function's local variables, the value of `this`, and the return address. This is why deep recursion eats memory: every frame takes space, and none are freed until the function returns. ### Stack overflow No base case in a recursive function means frames pile up forever. ```javascript function countdown(n) { console.log(n); countdown(n - 1); // never stops } countdown(10000); // RangeError: Maximum call stack size exceeded ``` V8 (Chrome, Node.js) typically throws around 10,000 to 12,000 frames deep. The fix is a base case: ```javascript function countdown(n) { if (n < 0) return; // base case console.log(n); countdown(n - 1); } ``` ### Single-threaded execution JavaScript has one call stack. That is a design choice, not a constraint. It makes code predictable: no race conditions, no shared mutable state, no locks needed. But it means one heavy operation blocks everything. A `while` loop running for 5 seconds freezes the tab because the stack never empties and no other code can run. CPU-heavy work belongs in a Web Worker. ### Call stack and the event loop The [event loop](/questions/event-loop-in-javascript) watches the call stack. When it is empty, the loop picks the next callback from the task queue and pushes it onto the stack. Async callbacks, timers, and Promise handlers all wait in the queue until the stack clears. ```javascript console.log('start'); setTimeout(() => { console.log('timeout'); // queued, runs after stack clears }, 0); console.log('end'); // Output: // start // end // timeout ``` Even with 0ms delay, `setTimeout` runs after `end`. The callback waits in the queue while `console.log('end')` is still on the stack. ### Reading error stack traces When an error is thrown, the stack trace shows the call stack at that exact moment. Read it bottom-up for call order. ```javascript function a() { b(); } function b() { c(); } function c() { throw new Error('oops'); } a(); // Error: oops // at c (file.js:3) // at b (file.js:2) // at a (file.js:1) ``` Bottom of the trace (`a`) is where execution started. Top (`c`) is where it crashed. I find this trips up developers who read only the first line and stop there. ### Common mistakes **1. Thinking async callbacks run on the current stack** ```javascript function fetchData() { fetch('/api/data').then(res => { console.log('inside then'); }); console.log('after fetch'); } fetchData(); // Output: // after fetch // inside then <- runs AFTER fetchData has already left the stack ``` The `.then` callback runs after `fetchData` popped off. It was waiting in the microtask queue. **2. Assuming `setTimeout(fn, 0)` fires immediately** Zero milliseconds means "after the current stack is empty," not "right now." If the stack stays busy for 2 seconds, the callback waits those 2 seconds plus the 0ms delay. **3. Blocking the stack with synchronous loops** ```javascript const start = Date.now(); while (Date.now() - start < 3000) {} // busy-wait for 3 seconds console.log('done'); // No event, click, or timer fired during those 3 seconds ``` Move expensive work to a Web Worker or break it into chunks with `setTimeout`. **4. Misreading stack traces** Stack traces read bottom-to-top by call order, but the error is at the top. Read the whole trace to find where the call originated. ### Real-world usage - **Browser DevTools:** the "Call Stack" panel shows the live stack as you step through code - **React:** component render functions appear on the stack during reconciliation; render errors show a component stack trace - **Express:** middleware chains are nested calls; unhandled errors travel up the stack to the error handler - **Node.js:** `Error.captureStackTrace` captures the call stack at any point for custom diagnostics ### Follow-up questions **Q:** What happens when the call stack is empty? **A:** The event loop checks the microtask queue first (Promise callbacks), drains it completely, then takes one task from the macrotask queue (setTimeout, setInterval) and pushes it onto the stack. **Q:** Why does a long `for` loop block UI in the browser? **A:** The loop keeps the call stack occupied the entire time. The event loop cannot push any callbacks (clicks, repaints, timers) until the stack clears. Break the work into chunks using `setTimeout` or `requestAnimationFrame`. **Q:** Does `async/await` interact with the call stack differently? **A:** Code before the first `await` runs on the stack normally. At `await`, the function suspends and its frame is removed from the stack. When the awaited Promise resolves, the function resumes as a microtask and a new frame is pushed for the code after `await`. **Q:** What exactly is a stack frame? **A:** A block of memory the engine allocates for one function call. It holds local variables, the arguments object, the `this` binding, and a return address. The frame is freed when the function returns. **Q:** Can you increase the maximum stack size? **A:** In Node.js, yes: `node --stack-size=65536 app.js`. In browsers, no. The practical fix for deep recursion is to rewrite it iteratively or use trampolining. ## Examples ### Basic stack trace ```javascript function add(a, b) { return a + b; // stack: [add] } function calculate(x, y) { const result = add(x, y); // stack: [calculate, add] -> [calculate] return result; } function main() { const total = calculate(5, 3); // stack: [main, calculate] -> [main] -> [] console.log(total); // 8 } main(); ``` `main` calls `calculate`, which calls `add`. Each call pushes a frame. Each return pops one. After `main` exits, the stack is empty and the output is `8`. ### Order processing with error tracking ```javascript function processOrder(order) { validateOrder(order); const total = calcTotal(order); notifyUser(order, total); } function validateOrder(order) { if (!order.items || order.items.length === 0) { throw new Error('Order has no items'); // Stack at this point: [validateOrder, processOrder] } } function calcTotal(order) { return order.items.reduce((sum, item) => sum + item.price, 0); } function notifyUser(order, total) { console.log(`Order ${order.id} confirmed. Total: $${total}`); } processOrder({ id: 42, items: [{ price: 19.99 }, { price: 5.50 }], email: 'user@example.com' }); // Order 42 confirmed. Total: $25.49 ``` If `validateOrder` throws, the error bubbles up through `processOrder`. The stack trace shows both frames, making the bug easy to locate. ### Async code and the stack ```javascript async function getUserData(userId) { // Stack: [getUserData] while this line starts const response = await fetch(`/api/users/${userId}`); // Stack is empty during fetch - getUserData is suspended // Stack: [getUserData] resumes here when the Promise resolves return response.json(); } async function renderProfile(userId) { const user = await getUserData(userId); console.log(`Rendering profile for ${user.name}`); } renderProfile(1); // Stack is not blocked during fetch; other code can run ``` The stack is not blocked during `fetch`. The function suspends, freeing the stack for other work. That is the whole point of [async/await](/questions/async-await-in-javascript).For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.