2 min read

The Event Loop

JavaScript is a single-threaded language, meaning it can only do one thing at a time. However, it can perform non-blocking operations (like fetching data or waiting for a timer) using the Event Loop.

1. The Call Stack

The Call Stack is a data structure that records where in the program we are.

  • If we step into a function, we put it on the top of the stack.
  • If we return from a function, we pop it off the top of the stack.
function multiply(a, b) {
  return a * b;
}

function square(n) {
  return multiply(n, n);
}

function printSquare(n) {
  var squared = square(n);
  console.log(squared);
}

printSquare(4);

2. Web APIs

When you call setTimeout, fetch, or DOM events, these are not part of the V8 JavaScript engine. They are Web APIs provided by the browser.

  • When you call setTimeout, the browser handles the timer.
  • When the timer finishes, the browser pushes the callback function to the Callback Queue.

3. The Callback Queue (Task Queue)

This is a queue of callback functions waiting to be executed.

4. The Event Loop

The Event Loop has one simple job: to monitor the Call Stack and the Callback Queue.

  1. If the Call Stack is empty, it takes the first event from the queue and pushes it onto the Call Stack, which effectively runs it.
console.log('Start');

setTimeout(() => {
  console.log('Callback');
}, 0);

console.log('End');

// Output:
// Start
// End
// Callback

Even though the timeout is 0, the callback is pushed to the Web API, then the Queue. The Event Loop must wait for the stack (containing console.log('End')) to clear before pushing the callback onto the stack.

5. Microtasks vs. Macrotasks

There are actually two queues:

  1. Macrotask Queue (Task Queue): setTimeout, setInterval, I/O.
  2. Microtask Queue: Promises (.then/.catch/.finally), queueMicrotask, MutationObserver.

Priority: The Event Loop prefers the Microtask Queue. It will process all microtasks before moving on to the next macrotask.

console.log('Start');

setTimeout(() => {
  console.log('Timeout (Macrotask)');
}, 0);

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

console.log('End');

// Output:
// Start
// End
// Promise (Microtask)
// Timeout (Macrotask)

programming/javascript/vanilla/javascript programming/asynchronous-programming