The JavaScript engine
Tasks and microtasks
Understand how event callbacks, timers, promise reactions, and rendering opportunities are scheduled around the JavaScript stack.
8 minute lesson
The event loop decides when queued JavaScript may use the main thread. A click handler, a timer callback, and the initial execution of a script are examples of tasks. Promise reactions and queueMicrotask() callbacks use the microtask queue.
Predict this output before running it:
console.log('start')
setTimeout(() => console.log('task'), 0)
Promise.resolve().then(() => console.log('microtask'))
console.log('end')
The result is:
start
end
microtask
task
The current task runs to completion, so both synchronous logs come first. When its call stack is empty, the browser drains queued microtasks. The timer callback belongs to a later task even though its delay is zero.
Between tasks, after microtasks have run, the browser may get a rendering opportunity. That word “may” matters: a timer is not a promise that a frame will be painted immediately.
Microtasks are useful for finishing a small piece of related work before other tasks. They are dangerous when they keep adding more microtasks:
function repeat() {
queueMicrotask(repeat)
}
repeat()
The queue never drains, so input, timers, and rendering can be starved. Keep microtasks bounded. When work is large, split it across later tasks or move suitable computation to a worker.
Lesson completed