Event Loop
A programming pattern that waits for and dispatches events in a single thread. Node.js uses it to handle thousands of concurrent connections without creating a thread per connection.
What is Event Loop?
In short
An event loop is a single-threaded loop that waits for events like finished network reads or expired timers, then runs the callback registered for each one before looping again. It lets one thread handle thousands of concurrent connections by never blocking on slow I/O, which is how Node.js, Nginx, and browser JavaScript stay responsive.
What an event loop actually is
Most servers handle concurrency by giving each connection its own thread. A thread costs about 1 MB of stack memory and the operating system has to switch between them constantly, so 10,000 connections means 10,000 threads and a lot of wasted CPU on context switching.
An event loop takes the opposite approach. One thread runs a loop forever. On each pass it asks the operating system: which of the things I am waiting on are ready now? Maybe a socket has data, maybe a file read finished, maybe a 200ms timer expired. For each ready event it calls the function you registered earlier, runs that function to completion, then goes back and asks again.
The key rule is that callbacks must be short and must never block. If a callback sits and waits, the whole loop freezes and every other connection stalls behind it. So slow work, reading a file, querying a database, calling another API, is started and handed off, and a callback fires later when the result arrives.
How it works under the hood
The loop sits on top of an operating system feature for watching many file descriptors at once. On Linux that is epoll, on macOS and BSD it is kqueue, on Windows it is IOCP. You hand the kernel a list of sockets you care about and it tells you, in one call, which ones are ready. This is why the cost stays flat as connections grow instead of one syscall per socket.
Node.js builds this with a C library called libuv. libuv runs the loop in distinct phases on every tick: timers that are due, then pending I/O callbacks, then poll for new I/O, then setImmediate callbacks, then close callbacks. Between phases it also drains microtasks like resolved Promises and process.nextTick, which run before the loop moves on.
There is a catch. True non-blocking I/O does not exist for everything. File system reads, DNS lookups, and CPU-heavy crypto cannot be done with epoll, so libuv keeps a small background thread pool, four threads by default, and offloads that work there. The single loop thread only handles the network sockets and the orchestration.
When to use it and the trade-offs
Event loops shine when the work is I/O bound: an API gateway, a chat server, a proxy, anything that mostly waits on the network or a database. One process can hold tens of thousands of open connections on a few hundred megabytes of memory, which a thread-per-connection model could never do affordably.
The weakness is CPU bound work. Image resizing, parsing a 50 MB JSON file, or a tight number-crunching loop will hold the single thread and block every other request for as long as it runs. The fix is to move that work off the loop, into a worker thread, a child process, or a separate service.
Compared to threads, an event loop trades simple-looking sequential code for callback or async/await code, and it gives up the easy multi-core parallelism you get for free with threads. To use all CPU cores you run multiple loop processes, one per core, behind a load balancer. Node uses its cluster module or PM2 for exactly this.
Where it is used in production
Node.js
Its entire runtime is an event loop powered by libuv, which is why a single Node process can serve thousands of concurrent HTTP connections.
Nginx
Each worker process runs an event loop on epoll, letting one worker handle tens of thousands of connections, the reason it scales so far better than Apache prefork.
Redis
Runs commands on a single-threaded event loop, so every operation is processed serially with no locking, which is part of why it is fast and predictable.
Browser JavaScript
Every browser runs a single event loop that processes the call stack, then the microtask queue, then the next task like a click or a network callback.
Frequently asked questions
- Is the event loop single-threaded?
- The loop itself runs on one thread, and your JavaScript callbacks all run there. But the runtime around it is not purely single-threaded. Node's libuv keeps a background thread pool, four threads by default, for file I/O, DNS, and crypto that cannot be done with epoll.
- What is the difference between the event loop and async/await?
- The event loop is the underlying mechanism that runs callbacks when events are ready. async/await is just nicer syntax over Promises that lets you write code that looks sequential while still handing control back to the loop at every await. Same engine, friendlier surface.
- What blocks the event loop?
- Any synchronous work that does not return quickly: a long for loop, JSON.parse on a huge payload, synchronous file reads like fs.readFileSync, or heavy crypto run on the main thread. While that runs, no other callback can fire and every connection waits.
- What are microtasks versus macrotasks?
- Macrotasks are full event-loop work items like timers, I/O callbacks, and setImmediate. Microtasks are resolved Promises and process.nextTick. The loop drains the entire microtask queue after each macrotask and before moving to the next phase, so microtasks always run sooner than the next timer or I/O callback.
- How does an event loop use multiple CPU cores?
- A single loop uses one core. To use all cores you run several loop processes, one per core, and put a load balancer in front. Node does this with its cluster module or a process manager like PM2, and Nginx runs one worker process per core by default.
Learn Event Loop hands-on
This page explains the idea. The full lesson lets you step through the ring as servers join and leave, read the implementation, and check yourself with a quiz. It is one of 760+ lessons in the System Design Masterclass, from your first API call to distributed consensus. Eleven Foundation lessons are free, no signup. Lifetime access is ₹499 in India or $7.99 worldwide, one payment, no subscription.
See also
Related glossary terms you might want to look up next.
Asynchronous
A communication model where the caller fires off a request and continues without waiting for a response. Essential for non-blocking I/O and event-driven systems.
Throughput
The number of operations a system can handle per unit of time. Think of it as how many cars a highway can move per hour.
Latency
The time delay between sending a request and getting a response. Amazon found every 100ms of extra latency costs 1% in sales.
Bandwidth
The maximum amount of data that can be transferred over a network in a given time. It's the width of the pipe, not how fast the water flows.
TCP
A reliable transport protocol that guarantees data arrives in order and without errors. It uses a three-way handshake to establish connections.
HTTP
The protocol powering the web. A request-response model where clients ask for resources and servers respond. Stateless by design.