The Task (Macrotask) Queue
Introduction
The task queue, commonly called the macrotask queue in interviews, holds callbacks for future event-loop turns: timers, UI events, message events, and other host-driven work.
A task is bigger than a microtask. The event loop runs one task, lets it finish completely, drains all microtasks created by that task, and only then chooses another task.
Why This Matters
Most async output puzzles depend on recognizing when something becomes a future task. Timer callbacks do not interleave with synchronous code, and a timer scheduled inside another timer usually goes behind already queued timer tasks. This is the difference between merely knowing setTimeout and actually tracing the queue.
Theory
What counts as a task
The initial script is a task. After it starts, it runs to completion. Other common task sources include setTimeout, setInterval, DOM event callbacks, postMessage, MessageChannel, and many I/O completion callbacks in host environments.
The term macrotask is popular in tutorials and interviews. The HTML specification talks about task queues and task sources. In interviews, saying task queue, often called macrotask queue is both practical and accurate.
FIFO within a source, not a global guarantee for everything
For simple interview snippets with multiple setTimeout(..., 0) calls, callbacks generally run in the order they are queued. Real browsers have multiple task sources and scheduling policy, so do not overstate that there is one universal FIFO queue for every possible async source.
Task versus microtask
A task represents a new event-loop turn. A microtask is a checkpoint that runs after the current stack clears and before the next task. Therefore, if a timer callback schedules a promise callback, that promise callback runs before the next timer callback.
Nested timers
If task A schedules a new timer while task B is already queued, the new timer does not jump ahead of B. It becomes a future task. This is a common interview trick.
Rendering and responsiveness
Browsers may render between tasks after microtasks drain. Long tasks block input, rendering, and timers because the event loop cannot move on while the call stack is busy. Performance tooling often calls any task longer than about 50ms a long task.
Visual Diagrams
Task queue | v +------------------+ microtask checkpoint +------------------+ | Run one task | -----------------------------> | Drain microtasks | | until stack empty| | until empty | +------------------+ +------------------+ | v Browser may render, then pick another task
Only one task is executed at a time, but all microtasks drain after it.
Initial script queues: task A: timer A task B: timer B Run task A: logs A queues task C: nested timer C Queue is now: task B task C
A nested timer is appended after already queued timer work.
Code Examples
Timer task schedules a microtask
The microtask created inside the first timer drains before the second timer task.
Long tasks block the next task
A busy synchronous task prevents timers and event handlers from running. Avoid this shape in production UI code.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1 2console.log('one');3 4setTimeout(function () {5 console.log('two');6}, 0);7 8setTimeout(function () {9 console.log('three');10}, 0);11 12Promise.resolve().then(function () {13 console.log('four');14});15 16console.log('five');Predict the output #2
1 2setTimeout(function () {3 console.log('timer A');4 5 setTimeout(function () {6 console.log('timer C');7 }, 0);8}, 0);9 10setTimeout(function () {11 console.log('timer B');12}, 0);13 14console.log('sync');Coding Exercises
Yield work in chunks
MediumImplement processInTasks(items, size, work, done) so it processes size items synchronously, then yields with setTimeout(..., 0) before processing the next chunk. Call done() after all items are processed.
Interview Questions
1What is a macrotask, and how is it different from a microtask?
A macrotask, more precisely a task, is a full event-loop turn such as the initial script, a timer callback, or an event callback. A microtask is a smaller callback checkpoint, such as a promise reaction, that runs after the current task's stack is empty and before the next task starts.
2If a timer callback schedules another timer, can the nested timer run before an already queued timer?
In the normal timer examples interviewers use, no. The nested timer is scheduled only when the first timer task runs, so it is queued behind timer tasks that were already eligible and waiting.
Follow-ups
- What if the first timer schedules a promise instead?
- Why can long tasks hurt UI responsiveness?
Quiz
1. Which item is typically a task/macrotask?
2. What happens after a timer task finishes before the next timer task runs?
Summary
- Tasks are full event-loop turns; timer and event callbacks are common task sources.
- The initial script itself runs as a task.
- After one task completes, all microtasks drain before the next task.
- Nested timers are future tasks and do not jump ahead of already queued work.
Cheat Sheet
Task/macrotask examples: initial script, setTimeout, setInterval, DOM events, message events.
After every task: call stack empty → drain all microtasks → maybe render → next task.
Nested timer trick: a timer scheduled inside timer A goes behind timer B if B was already queued.
Performance: long tasks block input, rendering, and async callbacks.