Event Loop
An event loop drives asynchronous work by repeatedly taking ready callbacks or events and running them to completion on one thread.
Definition
An event loop is a single-threaded scheduler that processes a queue of events or callbacks: it waits for I/O to become ready, runs the associated handler to completion, then returns to the queue. Non-blocking I/O allows one thread to serve many connections, because waiting is delegated to the operating system rather than occupying a thread. The model is cooperative — a handler that blocks stalls the whole loop.
Context and scope
The concurrency model of Node.js, browsers, and many async runtimes, where huge numbers of I/O-bound operations share one thread.
Common misconceptions
- One thread cannot be concurrent — it can be highly concurrent for I/O, because waiting happens in the OS, not in the thread
- Blocking code is fine if it is short — any synchronous CPU work stalls every other task on the loop, not just its own
- The event loop makes everything parallel — it provides concurrency for I/O, but CPU-bound work still needs worker threads or processes
Anti-patterns
- Running heavy computation or synchronous file I/O directly on the event loop
- Awaiting work that never resolves, silently pinning memory and leaving a stalled chain
- Assuming timers and promises fire in exactly the order they were queued, ignoring microtask and macrotask phases
Related concepts
Related examples
Authoritative source
What it is
An event loop is a loop that owns one thread and a queue of pending work. It asks the operating system which file descriptors are ready, runs the callbacks for those, then drains microtasks such as resolved promises before the next iteration. Each callback runs to completion — there is no preemption — so the loop never needs locks around shared state, and a single thread can juggle thousands of connections.
Why it matters
Threads are expensive and most server work is spent waiting on I/O, not computing. By not dedicating a thread to each wait, an event loop serves far more connections with far less memory. It also removes most data races by construction, because callbacks do not run in parallel. This is why Node.js and browser JavaScript reach high I/O concurrency on one thread.
How to use it well
Never block the loop: offload CPU-heavy work and synchronous file access to worker threads or a separate process. Understand the phase ordering — timers, I/O callbacks, microtasks — because it changes when code runs relative to promise resolution, and avoid starving the loop with long callback chains. Guard against unhandled rejections that can silently halt a chain, and keep callback bodies short so latency stays bounded.