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 fully writing your Express.js backend code without having to deal with loops of any kind - not to mention event loop.
Let's take a look at event loop in a general and platform agnostic way. This way we'll gather the raw materials to dwell deeper into the Node.js event loop later. It has some custom tailored nifty features to make it even greater.
Two types: events and event handlers
The two main characters in this show are
- event handlers, subscribers to those events
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 occurs.
Event loop takes event and fires handlers
The responsibility of the event loop is to repeatedly wait for an event to happen and then execute in turn every handler that has subscribed to that particular event. After every handler has been executed the event loop starts again from the beginning waiting for the next event to happen and 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 concurrent mode and realize the following: all this execution happens sequentially in single thread.
If there are two event handlers registered for the same event, the latter will be executed only after the first has finished. There will be no checking for new event until after that latter event handler has finished executing. Everything happens sequentially in 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 file is ready to be read, the event handler for that event schedules a write that pushes that data to pending HTTP request that is being served, and after that write is complete, the next chunk of data is scheduled to be read from the file. This way the event loop keeps alive and pumping.
Events happen and they fire event handlers that in turn register yet more events and so on. This cycle is key to the event loop control flow - 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 - well, how exactly does that happen. You're right, that parts needs some operating system magic. Fortunately there are multiple ways available to achieve this depending on the operating system (select, epoll, kqueue, IOCP). This is normally abstracted away from everyday use by the event loop library (libuv in Node.js case).
One another thing is also needed. Naturally for the event loop to be useful there needs to be way to subscribe to events i.e register code to be executed when a particular event happens. This something that has to be available in the event loop implementation (libuv in Node.js case).
Event loop in Node.js
Event loop is very core part of Node.js and responsible for many of the characteristics of Node - and there exists both good and bad traits. The superior performance of handling vast I/O heavy loads (a web server for example) and also the lack of good long and informative stack traces for thrown exceptions can be all tracked down to the existence of 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 and it keeps pumping until there are no more event handlers left to be executed. The loop runs in single thread and event handlers are executed sequentially one after another.