Understanding the event loop mechanism in Nodejs

Even though V8 is single-threaded, the underlying C++ API of Node isn’t. It means that whenever we call something that is a non-blocking operation, Node will call some code that will run concurrently with our javascript code under the hood. Once this hiding thread receives the value it awaits for or throws an error, the provided callback will be called with the necessary parameters.

When Node.js starts, it initializes the event loop, processes the provided input script which may make async API calls, schedule timers, or call process.nextTick(), then begins processing the event.

Task queue

Javascript is a single-threaded, event-driven language. This means that we can attach listeners to events, and when a said event fires, the listener executes the callback we provided.

Whenever you call setTimeout, http.get or fs.readFile, Node.js sends these operations to a different thread allowing V8 to keep executing our code. Node also calls the callback when the counter has run down or the IO / http operation has finished.

These callbacks can enqueue other tasks and those functions can enqueue others and so on. This way you can read a file while processing a request in your server, and then make an http call based on the read contents without blocking other requests from being handled.

However, we only have one main thread and one call-stack, so in case there is another request being served when the said file is read, its callback will need to wait for the stack to become empty. The limbo where callbacks are waiting for their turn to be executed is called the task queue (or event queue, or message queue). Callbacks are being called in an infinite loop whenever the main thread has finished its previous task, hence the name ‘event loop’.

The following diagram shows a simplified overview of the event loop’s order of operations

Each phase has a FIFO queue of callbacks to execute. While each phase is special in its own way, generally, when the event loop enters a given phase, it will perform any operations specific to that phase, then execute callbacks in that phase’s queue until the queue has been exhausted or the maximum number of callbacks has executed. When the queue has been exhausted or the callback limit is reached, the event loop will move to the next phase.

Phases overview:
1) timers: this phase executes callbacks scheduled by setTimeout() andsetInterval().

2) pending callbacks: executes I/O callbacks deferred to the next loop iteration.

3) idle, prepare: only used internally.

4) poll: retrieve new I/O events; execute I/O related callbacks (almost all with the exception of close callbacks, the ones scheduled by timers, and setImmediate()); node will block here when appropriate.

5) check: setImmediate() callbacks are invoked here.

6) close callbacks: some close callbacks, e.g. socket.on('close', ...).

Between each run of the event loop, Node.js checks if it is waiting for any asynchronous I/O or timers and shuts down cleanly if there are.

Understanding process.nextTick

In Node.js, each iteration of an Event Loop is called a tick. To schedule a callback function to be invoked in the next iteration of the Event Loop, we use process.nextTick(). It just takes a callback with no time bound, since it will be executing in the next iteration of the Event Loop.

Any time you call process.nextTick() in a given phase, all callbacks passed to process.nextTick() will be resolved before the event loop continues. This can create some bad situations because it allows you to "starve" your I/O by making recursive process.nextTick() calls, which prevents the event loop from reaching the poll phase.

process.nextTick() vs setImmediate

  • process.nextTick() fires immediately on the same phase
  • setImmediate() fires on the following iteration or 'tick' of the event loop

setInterval vs setTimeout

  • setInterval fires again and again in intervals, while setTimeout only fires once.

setImmediate

setImmediate() is designed to execute a script once the current poll phase completes.

Example:test.js

setImmediate(() => {
console.log(‘immediate’)
});

setTimeout(() => {
console.log(‘timeout’)
},0);

process.nextTick(()=>console.log(‘next’));

console.log(‘hey’);

Output

Hey
next
timeout
immediate

Suffering from Knowledge Quest

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store