Skip to main content

Runtime

JavaScript Runtime

Heap

  • it stores the value of the variables, functions.
  • Stores dynamic, variable-size objects.
  • Slower access.
  • Used for non-primitives (objects, arrays, functions).
  • The references stored in the stack, but the actual object/array data lives in the heap.
Stack:
obj -> reference (pointer to heap)
arr -> reference (pointer to heap)

Heap:
{ name: "Masum" }
[1,2,3]

Callstack

  • its also called Execution Context Stack
  • whenever a new execution context is created(a function is invoked), it is pushed onto the stack
  • when a function is completes, its execution context is popped off the stack
  • Stores fixed-size, simple data.
  • Fast access.
  • Used for primitives and references to objects.

Stack vs Heap

Stack (fast, fixed size):
+-------+
| x = 10|
| y = 20|
| obj -> pointer to heap |
+-------+

Heap (dynamic, big):
+---------------------+
| { name: "Masum" } |
| [1,2,3] |
+---------------------+
  • When you copy a primitive, a new copy is made.
  • When you copy a non-primitive, the reference is copied, not the object itself.

var, let, and const don’t define where (heap or stack) a value is stored; they only define the variable’s scope and, in the case of const, the immutability of the binding. The actual storage depends on the type of value: primitives go on the stack, and objects/arrays/functions go on the heap.

Event Loop

V8 only executes JavaScript — it doesn’t include Web APIs or the event loop itself.

  • In Chrome → V8 + Blink (browser runtime)
  • In Node.js → V8 + libuv (event loop + async I/O)

So:

  • V8 runs your JS synchronously.
  • Asynchronous tasks (timers, fetch, I/O) are handled by the host environment and return results back to V8 via callback queues.

The Event Loop continuously checks:

  1. The Call Stack → runs synchronous code
  2. The Microtask Queue → runs microtasks (Promises, async/await, etc.)
  3. The Macrotask Queue (also called the Task Queue) → runs tasks like setTimeout, fetch, and events

Queue

A queue is a general term for a list of tasks waiting to be executed in order (FIFO — First In, First Out).

In JavaScript:

  • There are different queues for different types of asynchronous work:
    • Microtask Queue
    • Macrotask Queue (Task Queue)

So when people say “the queue,” they usually mean one of these two queues managed by the Event Loop.

Microtasks

Microtasks are small, high-priority asynchronous tasks that execute immediately after the current synchronous code — before the browser renders or any macrotasks run.

  • Promise.then(), Promise.catch(), Promise.finally()
  • async/await (code after await)
  • queueMicrotask()
  • MutationObserver

When They Run:

After the current synchronous task finishes and before any macrotask starts.

Macrotasks

Macrotasks (also called tasks) are larger asynchronous tasks that are scheduled to run after all microtasks have finished.

  • setTimeout()
  • setInterval()
  • setImmediate() (Node.js)
  • I/O callbacks
  • fetch().then() (fetch callback itself is microtask)
  • DOM events like click or scroll

When They Run:

  • After all synchronous code and all microtasks are completed.
  • Only one macrotask runs per event loop cycle.

Combined Example

console.log("1");

setTimeout(() => console.log("2 (Macrotask)"), 0);

Promise.resolve().then(() => console.log("3 (Microtask)"));

console.log("4");
StepActionQueue
1"1" loggedSynchronous
2setTimeout() added to Macrotask QueueMacrotask
3Promise.then() added to Microtask QueueMicrotask
4"4" loggedSynchronous
5Stack empty → run Microtask Queue"3" logged
6Then run Macrotask Queue"2" logged

The Microtask Queue and the Macrotask Queue in JavaScript are both regular FIFO (First-In, First-Out) queues in structure — but the Event Loop gives higher priority to the Microtask Queue when deciding what to execute next.