Why Does JavaScript Have an Event Loop? Uncovering Its Origins and Mechanics
This article explores the origins and purpose of the JavaScript event loop, explains how browsers and Node.js implement it with external and internal queues, compares their differences, and illustrates the concepts through detailed code examples and visual diagrams.
Many articles discuss what the Event Loop is, but few explain why JavaScript has one. This article investigates the origin of the event loop and its purpose.
It studies JavaScript's event loop from three angles: why it exists, what it is, and the differences between browsers and Node.js.
Why an Event Loop
JavaScript was created by Netscape for more complex web interactions. Its creator Brendan Eich wrote the first version in ten days, and the original design did not consider an event loop. The need arose from typical web scenarios such as client‑side form validation before submission.
In that scenario, JavaScript code is triggered by browser events (e.g., a click on a submit button). The code is invoked passively, and the event loop defined in the HTML standard coordinates user interaction, scripts, rendering, networking, and other activities.
To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section. Each agent has an associated event loop, which is unique to that agent.
Thus, the event loop is a mechanism used by the user agent (browser) to coordinate various kinds of events, not a feature of the JavaScript language itself.
What the Event Loop Is
The event loop is a coordination mechanism inside a user agent. From a developer’s perspective it consists of two queues:
Task Queue (external queue) : the browser‑managed queue for events such as DOM operations, user interaction, network requests, History API, and timers.
Microtask Queue (internal queue) : the JavaScript‑managed queue for Promise callbacks, MutationObserver, and the now‑deprecated Object.observe.
Although called queues, they behave more like ordered sets because tasks are only removed when their conditions are satisfied.
External Queue
The external (Task) queue contains events originating from the browser:
DOM operations (page rendering)
User interaction (mouse, keyboard)
Network requests (Ajax, etc.)
History API operations
Timers (setTimeout, etc.)
Each external source may have its own queue with different priorities.
Internal Queue
The internal (Microtask) queue holds JavaScript‑only tasks such as:
Promise .then/.catch callbacks
MutationObserver callbacks
Object.observe callbacks (deprecated)
Processing Model
The simplified steps of the event loop are:
Take one executable task from the external queue and run it.
Execute all tasks in the internal queue.
Render the page.
Case Study
Consider the following code:
console.log('script start');
setTimeout(function(){ console.log('setTimeout'); }, 0);
Promise.resolve().then(function(){ console.log('promise1'); }).then(function(){ console.log('promise2'); });
console.log('script end');Output:
script start<br/>script end<br/>promise1<br/>promise2<br/>setTimeout<br/>The execution order is:
Execute the first console.log (script start).
Encounter setTimeout – add a task to the external queue.
Encounter two Promise then – add two microtasks to the internal queue.
Execute the second console.log (script end).
Run all microtasks (promise1, promise2).
Run the external task (setTimeout).
Understanding the separation of external and internal queues simplifies many async puzzles.
Browser vs. Node.js Event Loop Differences
Browsers embed JavaScript in the HTML event loop, while Node.js embeds it in libuv’s I/O loop. Consequently:
Node.js has no HTML rendering phase.
Node’s external sources include file and other I/O events instead of mouse/keyboard.
Node’s internal queue contains only Promise callbacks.
Older Node versions allowed multiple external tasks per loop, unlike browsers which process only one external task per iteration. Example:
setTimeout(() => { console.log('timer1'); Promise.resolve().then(() => console.log('promise1')); });
setTimeout(() => { console.log('timer2'); Promise.resolve().then(() => console.log('promise2')); });Browser output: timer1 → promise1 → timer2 → promise2.
Node (pre‑v11) output: timer1 → timer2 → promise1 → promise2.
Node introduced setImmediate as a separate external queue to address timer precision. Example:
setTimeout(() => { console.log('setTimeout1'); Promise.resolve().then(() => console.log('promise1')); });
setTimeout(() => { console.log('setTimeout2'); Promise.resolve().then(() => console.log('promise2')); });
setImmediate(() => { console.log('setImmediate1'); Promise.resolve().then(() => console.log('promise3')); });
setImmediate(() => { console.log('setImmediate2'); Promise.resolve().then(() => console.log('promise4')); });Output on Node 10.x:
setImmediate1<br/>setImmediate2<br/>promise3<br/>promise4<br/>setTimeout1<br/>setTimeout2<br/>promise1<br/>promise2<br/>Node’s event loop phases (timers, pending callbacks, idle/prepare, poll, check, close) are illustrated below:
In the check phase, setImmediate callbacks run. The poll phase handles I/O callbacks, and the pending‑callbacks phase limits the number of callbacks to avoid blocking the loop.
Summary
Browser environments prioritize user experience, so optimizations that reduce JavaScript execution time at the cost of longer rendering are usually avoided. Node.js, being server‑side, can afford different trade‑offs, such as executing multiple external tasks per loop to keep the process responsive.
The key takeaway is that the event loop is not a JavaScript language feature but a mechanism provided by the host environment (browser or Node) to coordinate JavaScript with other events.
Node Underground
No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
