The event loop is how a single-threaded runtime like JavaScript handles many things in progress at once. Slow work (timers, network, files) is done outside your code; when it finishes, a callback waits in a queue, and the event loop runs it only when the call stack is empty.
How it works
One turn of the loop in a browser, as the HTML Standard describes it:
- Run the oldest task from a task queue until the call stack is empty.
- Run every microtask, including microtasks added while doing so, until the microtask queue is empty.
- If it is time to render:
requestAnimationFramecallbacks, style, layout, paint. - Repeat.
Node.js uses the same idea but with named phases (timers, poll for I/O, check for setImmediate, close callbacks),
and runs process.nextTick callbacks and promise microtasks between callbacks.
Example
setTimeout(() => console.log('task'), 0);
Promise.resolve().then(() => console.log('microtask'));
console.log('sync');
// sync
// microtask
// taskTraps
setTimeout(fn, 0)is a minimum delay, not “now”.- Endless microtasks starve rendering; endless tasks do not.
- Heavy synchronous code blocks everything. Move it to a Web Worker or split it into chunks.
Related
Lesson: #1 · The event loop. Spec: HTML Standard: event loops.
Check yourself
What has to be true before a queued callback can run?
The call stack must be empty. Then all microtasks run first, and only after that the next task.
Why can a page stay responsive while it waits for a network request?
The request is handled by the browser outside the JavaScript thread. The thread is free until the response’s callback is queued and run.