JavaScript Interview Prep
Asynchronous JavaScript

Callback Queue

The Macrotask Line

LinkedIn Hook

Every setTimeout, every click handler, every setInterval tick — they all stand in the same line.

That line is called the Callback Queue (also known as the Task Queue or Macrotask Queue), and it has exactly one rule: first in, first out. But here's the twist interviewers love to exploit — the Event Loop only picks one macrotask per tick, and it checks the microtask queue between every single pick.

That's why three setTimeout(fn, 0) calls don't run "together." They run one at a time, interleaved with microtasks, with the browser potentially repainting in between.

If you've ever wondered why setInterval(fn, 1000) is not actually every 1000ms, or why your click handler feels laggy when the page is busy — the Callback Queue is the reason.

In this lesson you'll see the FIFO rule in action, walk through the "one-macrotask-per-tick" behavior, and understand why setInterval timing is a suggestion, not a guarantee.

Read the full lesson -> [link]

#JavaScript #EventLoop #Macrotask #CallbackQueue #InterviewPrep #AsyncJS #WebDevelopment


Callback Queue thumbnail


What You'll Learn

  • What goes into the Callback Queue (macrotasks) and why order matters
  • The "one macrotask per Event Loop tick" rule
  • Why setInterval is never guaranteed to fire exactly on time

FIFO — First In, First Out

The Callback Queue (also called the Task Queue or Macrotask Queue) is where callbacks from setTimeout, setInterval, DOM events, and I/O operations wait to be executed.

Think of it like a line at a bank. People (callbacks) join the line when they arrive, and they're served in FIFO order (First In, First Out). But the teller (call stack) must finish with the current customer before calling the next one.

What Goes Into the Callback Queue?

SourceExample
setTimeoutsetTimeout(() => {...}, 1000)
setIntervalsetInterval(() => {...}, 500)
DOM Eventsbutton.addEventListener('click', handler)
I/O OperationsFile reads (Node.js), XMLHttpRequest
setImmediateNode.js only
MessageChannelport.onmessage

FIFO Order in Action

setTimeout(() => console.log("First timeout"), 0);
setTimeout(() => console.log("Second timeout"), 0);
setTimeout(() => console.log("Third timeout"), 0);

console.log("Synchronous");

Output:

Synchronous
First timeout
Second timeout
Third timeout

All three setTimeout callbacks enter the Callback Queue in order. The Event Loop processes them one at a time, FIFO.

Important: One Macrotask Per Event Loop Tick

The Event Loop processes one macrotask per tick, then checks and drains the entire microtask queue before picking the next macrotask.

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

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

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

console.log("Sync");

Output:

Sync
Microtask 1
Macrotask 1
Macrotask 2

Step-by-step:

  1. Both setTimeout callbacks -> Callback Queue (macrotask)
  2. Promise .then -> Microtask Queue
  3. "Sync" -> prints immediately
  4. Call stack empty -> drain Microtask Queue first -> "Microtask 1"
  5. Pick one macrotask -> "Macrotask 1"
  6. Check microtask queue (empty) -> pick next macrotask -> "Macrotask 2"

setInterval — Repeated Macrotasks

let count = 0;

const intervalId = setInterval(() => {
  count++;
  console.log(`Interval tick: ${count}`);

  if (count === 3) {
    clearInterval(intervalId);
    console.log("Interval cleared");
  }
}, 1000);

console.log("Interval registered");

Output:

Interval registered
Interval tick: 1       <- after ~1 second
Interval tick: 2       <- after ~2 seconds
Interval tick: 3       <- after ~3 seconds
Interval cleared

Each interval tick adds a new callback to the Callback Queue. If the call stack is busy when a tick fires, the callback waits — intervals are not guaranteed to be exactly on time.

Callback Queue visual 1


Common Mistakes

  • Thinking three setTimeout(fn, 0) calls run "in parallel." They run strictly one after the other in FIFO order, with microtask draining in between.
  • Expecting setInterval(fn, 1000) to fire exactly every 1000ms. If the stack is busy, callbacks bunch up or drop — use recursive setTimeout or requestAnimationFrame for precision.
  • Confusing the Callback Queue with the Call Stack. The stack is LIFO and synchronous; the queue is FIFO and async.

Interview Questions

Q: What is the Callback Queue?

The Callback Queue (also called Task Queue or Macrotask Queue) is a FIFO queue where callbacks from Web APIs (setTimeout, setInterval, DOM events, I/O) wait for the Event Loop to push them onto the call stack. The Event Loop only moves a callback to the call stack when the stack is completely empty and all microtasks have been processed.

Q: What's the difference between the Callback Queue and the Call Stack?

The Call Stack is where JavaScript executes code — it's LIFO (Last In, First Out) and runs synchronously. The Callback Queue is where async callbacks wait — it's FIFO (First In, First Out). The Event Loop transfers tasks from the queue to the stack, but only when the stack is empty.

Q: Does setInterval guarantee exact timing?

No. setInterval(fn, 1000) means "try to add a callback every ~1000ms," but if the call stack is busy when a tick fires, the callback waits in the queue. This can lead to callbacks bunching up or being delayed. For precise timing needs, use requestAnimationFrame or recursive setTimeout.

Q: How many macrotasks does the Event Loop process per tick?

Exactly one. After each macrotask finishes, the Event Loop drains the entire microtask queue before returning to pick the next macrotask.


Quick Reference — Cheat Sheet

CALLBACK QUEUE (MACROTASK) — QUICK MAP

Order:        FIFO (first in, first out)
Per tick:     ONE macrotask, then drain microtasks, repeat

Sources:
  setTimeout / setInterval
  DOM events (click, scroll, keydown, ...)
  I/O (XHR, fs.readFile in Node)
  setImmediate (Node)
  MessageChannel (port.onmessage)

Timing warning:
  setInterval(fn, 1000) != exactly every 1000ms
  busy stack -> callback waits -> bunching/dropping possible

Previous: Web APIs & Node APIs -> The Runtime's Toolbox Next: Microtask Queue -> The Priority Lane


This is Lesson 3.3 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page