Skip to main content

Task queue in JavaScript

The task queue is a data structure that holds tasks (callbacks) waiting to run once the call stack is free. It is a key part of the Event Loop machinery in JavaScript.

Theory

TL;DR

  • The task queue holds callbacks that are ready to run but are waiting for the call stack to become free.
  • JavaScript is single threaded, so only one task runs at a time.
  • The Event Loop takes the first task from the queue (FIFO) and pushes it onto the stack as soon as the stack is empty.
  • There are in fact two queues: the macrotask queue (setTimeout, DOM events) and the microtask queue (Promise.then, queueMicrotask).
  • Microtasks always run before the next macrotask.
  • The order is: the stack, then microtasks, then macrotasks.

Quick example

javascript
console.log('1'); setTimeout(() => { console.log('2'); }, 0); console.log('3');

Console output:

text
1 3 2

The key idea

JavaScript is a single threaded language, it can run only one task at a time. While the engine is busy running functions on the call stack, all the other tasks, for example click handlers, setTimeout, network callbacks and so on, wait their turn in the task queue.

When the call stack becomes empty, the Event Loop takes the first task from the queue and places it on the stack. And so it continues, endlessly.

Walking through the example

  1. console.log('1') runs immediately and prints 1.
  2. setTimeout(...): the browser starts a timer. When the timer fires, the callback lands in the task queue rather than running instantly.
  3. console.log('3') runs and prints 3.
  4. Once the call stack is empty, the Event Loop takes the task from the queue (the setTimeout callback) and places it on the stack, which prints 2.

This is why a zero delay in setTimeout means "as soon as possible after the stack is free", not "right now".

Task queue vs microtask queue

JavaScript has two queues:

Queue typeSource of tasksWhen they run
Task Queue (macro tasks)setTimeout, setInterval, DOM events, I/O callbacks and so onAfter the stack and all microtasks are cleared
Microtask QueuePromise.then, queueMicrotask, MutationObserverRight after the current task, before the next macrotask

The difference matters: the microtask queue is drained completely, while only one task is taken from the macrotask queue per turn of the loop.

Example with microtasks

javascript
console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C')); console.log('D');

Output order:

text
A D C B

Explanation:

  • A, D run immediately, this is synchronous code on the call stack.
  • C is a microtask, so it runs right after the stack is cleared.
  • B is a macrotask, so it runs after all microtasks.

Terms at a glance

TermDescription
Task QueueThe macrotask queue, it holds callbacks from setTimeout, events and so on
Microtask QueueThe microtask queue, for Promise.then, queueMicrotask
Event LoopThe mechanism that watches the stack and the queues and decides what runs next
OrderFirst the stack, then microtasks, then macrotasks

Common mistakes

  • Assuming setTimeout(fn, 0) runs fn immediately. The callback only joins the task queue and waits for the stack to empty.
  • Confusing the task queue with the call stack. The stack is LIFO and holds what runs now; the queue is FIFO and holds what will run later.
  • Treating promises and timers as equals. Promise.then goes to the microtask queue and always beats any setTimeout, even one with a zero delay.
  • Spawning microtasks endlessly. If a microtask keeps scheduling another microtask, the queue never drains and macrotasks and rendering never get control.
  • Thinking the queue makes code parallel. Execution still goes through one thread and one stack, just in a different order.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.