Event loop from 10,000ft - core concept behind Node.js

You can't swing a cat in the Node.js world and not hear event loop mentioned. You're wondering what is so special about it and why should you care? Up to this point, you've been writing your Express.js backend code without having to deal with loops of any kind - not to mention the event loop.

Let's take a look at the event loop in a general and platform-agnostic way. This way, we'll gather the raw materials to dive deeper into the Node.js event loop later.

Two types: events and event handlers

The two main characters in this show are

  • events
  • event handlers

Events can be, for example, low-level operating system events such as "file is ready to be written" or "there's a fresh new HTTP request coming our way". Event handlers are pieces of program code that are meant to be executed when that specific event takes place.

Event loop takes events and fires handlers

Event loop diagram
Figure 1. Event loop repeatedly takes events and executes event listeners.

The responsibility of the event loop is to repeatedly wait for an event to happen and then execute a handler that has subscribed to that particular event. After the handler has been executed the event loop starts again from the beginning waiting for the next event to happen. It does this over and over again.

Today, when there's concurrency and multiple requests being handled simultaneously in every possible application program, it is easy to get confused at this point. Let's try to keep our minds from switching to parallel mode and realize the following: all this execution happens sequentially.

If a new event arrives mid-way of executing an event handler, that event will be handled later. There will be no checking for new events until an event handler has finished execution. Everything happens sequentially in a single thread.

Event handlers subscribe to yet more events

What is interesting is that most of the time, subscribing to events actually happens inside another event handler. For example, after a file is ready to be read, the event handler for that event schedules a write that pushes that data to pending HTTP request. After that write is complete, the next chunk of data is scheduled to be read from the file. This way, the event loop keeps pumping life into the application.

Events occur and fire handlers that in turn register yet more events and so on. This cycle is key to the program execution - even so much that Node.js exits as soon as there are no more event handlers registered.

Operating system magic required

Where do the events actually come from? The event loop is supposed to pick the next event. How exactly does that happen? If you've been thinking about that part, you're right. This needs some operating system magic. Behind the scenes, the event loop leverages operating system services to wait for events to take place. There are multiple ways to achieve this, depending on the operating system (select, epoll, kqueue, IOCP). Luckily, this is abstracted away from everyday use by the event loop library (libuv in Node.js case).

One other thing is also needed. Naturally, for the event loop to be useful, there needs to be a way to subscribe to events i.e. register code to be executed when a particular event happens. This is something that has to be available in the event loop implementation.

Event loop in Node.js

Event loop is a very core part of Node.js and responsible for many of the characteristics of Node - and there exist both good and bad traits. The superior performance of handling heavy I/O loads (a web server, for example) and also the lack of informative stack traces for thrown exceptions can be all traced back to the existence of an event loop. Even the asynchronous callback driven programming paradigm of Node.js directly descends from the event loop.

At the heart of every Node.js process exists an event loop. It keeps pumping as long as the process is alive and orchestrates the execution between different parts of your program and the operating system. The event loop is started as an infinite loop as the last thing in main function of the node binary. It keeps pumping until there are no more event handlers left to be executed. The loop runs in a single thread, and event handlers are executed to completion.

Related articles

Node doesn't wait for your database call to finish?

Node doesn't wait for your database call to finish?

Learn how asynchronous calls work and make your app run as you intended. Get short email course on asynchronicity and two chapters from Finish Your Node App.