The Task Queue

We know that synchronous function calls are pushed onto the Call Stack. But what happens to asynchronous callbacks when their timer finishes or their HTTP request succeeds?

Enter the Task Queue (also known as the “Message Queue” or “Macrotask Queue”).

The Golden Rule of the Event Loop

The task queue is a staging area where asynchronous callbacks wait in line to be processed. The Event Loop manages this queue with one very strict rule:

The Event Loop will ONLY push a callback from the Task Queue onto the Call Stack if the Call Stack is completely empty.

A Zero-Millisecond Delay

To prove this, look at what happens when we use setTimeout with a delay of exactly 0 milliseconds.

function startJob() {
  // Pushed to the Task Queue immediately
  setTimeout(() => {
    console.log("Hi I'm async!");
  }, 0);
  
  console.log("Job started");
  workOnJob();
}

function workOnJob() {
  console.log("Working on job");
}

startJob();

Because the delay is 0, you might expect "Hi I'm async!" to print first. However, this is the actual output:

Job started
Working on job
Hi I'm async!

Why did this happen?

  1. The setTimeout instantly pushes the callback into the Task Queue.
  2. However, startJob and workOnJob are still actively running on the Call Stack.
  3. The Event Loop sees the callback waiting, but it is strictly forbidden from interrupting the Call Stack.
  4. Only after workOnJob and startJob completely finish and the Call Stack becomes empty does the Event Loop move the callback onto the stack to execute.