Banner

Understanding the JavaScript event loop

Created Updated
4 min read 640 words

JavaScript is famously single-threaded — it executes one piece of code at a time in a synchronous manner. Yet, it handles asynchronous operations like timers, network requests, and DOM events gracefully without blocking. The secret behind this magic is the event loop, a core part of the JavaScript runtime that orchestrates concurrency.

Calls async API

Callback queued

Promise resolves

Call Stack Empty?

Run all Microtasks

Then next Macrotask

Start Script

Call Stack

Web API e.g., setTimeout, fetch

Macrotask Queue

Microtask Queue

Event Loop

Check Queues

In this article, we’ll explore how JavaScript’s event loop, call stack, and the two kinds of task queues — the microtask queue and macrotask queue — work together to enable asynchronous behavior in a single-threaded environment. By understanding this mechanism, you’ll be able to write more efficient and predictable async code.

The Call Stack: JavaScript’s Execution Engine

The call stack is at the heart of JavaScript’s execution model. Think of it like a stack of plates: when a function is called, it gets pushed onto the stack, and when it returns, it’s popped off.

  • The call stack tracks what function is currently executing.
  • Only one function runs at a time — JavaScript is single-threaded.
  • If the stack is empty, the event loop checks the queues for more work to do.

Web APIs and the Macrotask Queue

JavaScript doesn’t run alone — it’s embedded in a host environment like a browser or Node.js, which provides asynchronous capabilities via Web APIs (like setTimeout, fetch, or DOM events).

  • When you call an async API, it’s handled outside the call stack by the Web API environment.
  • Once completed, the callback is queued in the macrotask queue (a.k.a. the callback queue).
  • The event loop picks the next macrotask only after the call stack is empty and all microtasks are processed.

The Microtask Queue: Promises and More

The microtask queue (also known as the job queue) is another queue with higher priority than the macrotask queue.

  • Microtasks include .then() callbacks of Promises, queueMicrotask, and process.nextTick (Node.js).
  • After each task (macrotask or initial script) completes and the call stack clears, the event loop drains the microtask queue — running all its callbacks before touching the next macrotask.
  • This is why Promises often feel “faster” than setTimeout.

How the Event Loop Works

  1. Run the script’s synchronous code on the call stack.
  2. When the call stack is empty:
    • Run all microtasks from the microtask queue.
    • Then, take the next macrotask from the macrotask queue and push it onto the stack.
  3. Execute the macrotask.
  4. Repeat this cycle endlessly.

This system is what allows JavaScript to be asynchronous without the complexity of multithreading.

Event Loop in Action: A Simple Example

console.log("Start");

setTimeout(() => {
  console.log("Macrotask: setTimeout");
}, 0);

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

console.log("End");

Step-by-step execution:

  1. console.log("Start") → prints Start
  2. setTimeout schedules a callback in the macrotask queue
  3. Promise .then() schedules a callback in the microtask queue
  4. console.log("End") → prints End
  5. Call stack is now empty → event loop processes microtasks → prints Microtask: Promise
  6. Microtask queue is empty → event loop processes macrotasks → prints Macrotask: setTimeout

Output:

Start
End
Microtask: Promise
Macrotask: setTimeout

Summary: JavaScript’s Asynchronous Magic

  • The call stack runs synchronous code.
  • Web APIs handle async operations and enqueue callbacks in the macrotask queue.
  • Promises and similar features enqueue callbacks in the microtask queue, which always runs first.
  • The event loop coordinates all of this, ensuring a non-blocking experience despite JavaScript’s single-threaded nature.

Understanding how the event loop prioritizes tasks helps avoid surprises and lets you harness async JavaScript more effectively.

YouTube

Add comment below...

Please refresh the page if commenting system is not visible.
Further reading