Macrotasks
Macrotasks are larger units of work scheduled by the runtime.
They are often simply called tasks.
Common macrotasks include:
- script execution
setTimeoutcallbackssetIntervalcallbacks- DOM event callbacks
- message channel callbacks
For beginner event-loop reasoning, timers and events are the most important examples.
Timer Example
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
console.log("C");Output:
A
C
BThe timer callback is scheduled as a macrotask.
It cannot run until the current call stack is clear.
Macrotasks vs Microtasks
Microtasks run before the next macrotask.
console.log("Start");
setTimeout(() => {
console.log("Timer");
}, 0);
Promise.resolve().then(() => {
console.log("Promise");
});
console.log("End");Output:
Start
End
Promise
TimerThe timer is a macrotask.
The promise callback is a microtask.
After synchronous code finishes, microtasks run first.
setTimeout(..., 0) Does Not Mean Immediate
setTimeout(() => {
console.log("Later");
}, 0);This means:
Schedule this callback after at least 0ms, when the event loop can pick it.It does not interrupt current JavaScript.
It also waits behind microtasks.
DOM Events Are Tasks
In browsers, user events are also scheduled as tasks.
button.addEventListener("click", () => {
console.log("Clicked");
});When the user clicks, the runtime schedules the callback.
The callback runs when the call stack is clear and the event loop reaches that task.
setInterval
setInterval schedules repeated tasks.
const id = setInterval(() => {
console.log("Tick");
}, 1000);Each interval callback is a task.
If the main thread is busy, interval callbacks may be delayed.
Stop an interval with:
clearInterval(id);Timers Can Be Delayed
Timers are not guaranteed to run exactly on time.
setTimeout(() => {
console.log("Timer");
}, 100);
// Long blocking work
for (let i = 0; i < 1_000_000_000; i++) {}The timer cannot run while the stack is busy.
The delay means "not before this time", not "exactly at this time".
Task Cycle Simplified
A simplified browser event-loop cycle:
- Run one macrotask.
- Run all microtasks.
- Browser may render.
- Run the next macrotask.
This is simplified, but useful for beginner reasoning.
Best Practices
Do not rely on timers for exact precision.
Remember that microtasks run before timer callbacks.
Avoid long synchronous work that delays tasks.
Clear intervals when they are no longer needed.
Use requestAnimationFrame for animation-related work instead of guessing with timers.
Common Mistakes
Mistake 1: Assuming setTimeout(fn, 0) Runs Before Promises
Promises schedule microtasks.
Microtasks run before the next timer task.
Mistake 2: Expecting Timers to Be Exact
Timers can be delayed by long-running JavaScript, browser throttling, or runtime scheduling.
Mistake 3: Forgetting to Clear Intervals
Intervals keep running until cleared.
This can cause bugs and memory leaks.
Summary
Macrotasks are scheduled units of runtime work.
- Timer callbacks are macrotasks.
- DOM event callbacks are tasks.
setTimeout(..., 0)runs later, not immediately.- Microtasks run before the next macrotask.
- Timers can be delayed by blocking synchronous code.
- Clear intervals when they are no longer needed.