Event Loop In Node.js

Table of contents

What is an Event loop.

Tasks are executed in Last In First Out order (using a stack data structure) and these tasks are executed synchronously.

For Javascript to execute asynchronous task (tasks that require some amount of time to be executed) the Javascript V8 engine employs the use of the libuv, the libuv API is essentially for running asynchronous tasks.

  • How tasks are executed under the hood

All tasks are push into a call stack and then the synchronous are executed first in a LIFO order, during execution it if encounters an asynchronous task, it pushes the task into into the libuv for it be executed and continues it’s execution in the call stack.

When libuv is done resolving the task given to it, it moves the task into the event queue which is then handle by the event loop

  • So what is an event loop

The event loop is a mechanism that continuously checks for event in the event queue, if any, it pushes the callback into the call stack and ready to be executed. It is a C program that orchestrates or co-ordinates the execution of synchronous and asynchronous code in Node.js.

The event loop consist of 6 queues and these queues have their order of execution.

Queues in the event loop:

  1. nextTick() queue

  2. promise queue

The above queue group under the microtask queue.

  1. Timer queue

  2. input/output queue

  3. Check queue

    6.Closed queue


Order of execution

In the event loop:

All the APIs in the event loop queues executes callback functions

  • The microtask queues are always executed first.

The callbacks in the nextTick queue is executed first before the promise queue callbacks.

Promise.resolve().then(() => console.log('Promise callback 1'));
process.nextTick(() => console.log('Process nextTick 1'));

OUTPUT: 
Process nextTick 1
Promise callback 1
  • The timer queues callbacks are executed next and it consists of operations like setTimeout and setInterval API

      setTimeout(() => console.log('Inside Timeout'))
    
      Promise.resolve().then(() => console.log('Promise callback 1'));
      process.nextTick(() => console.log('Process nextTick 1'));
    
      OUTPUT:
      Process nextTick 1
      Promise callback 1
      Inside Timeout
    
  • input/Output(I/O) queue consist of operations like reading and writing to a file, using modules like the “fs” module. This queue is executed next after the timer queue

      const fs = require('fs')
    
      setTimeout(() => console.log('Inside Timeout'))
      fs.readFile(() => console.log('File read'))
    
      OUTPUT:
      Inside Timeout
      File read
    
  • Check queue consist of the setImmediate API, which always executed after the I/O queue but setImmediate callback always gets executed first due to I/O polling.

    I/O events are polled and the callback function are only added to the I/O queue only after the I/O is complete.

      const fs = require('fs')
    
      fs.readFile(() => console.log('File read'))
      setImmediate(() => console.log('Inside setImmediate'))
    
      OUTPUT:
      Inside setImmediate
      File read
    
  • Closed Queue are executed last after all other queues in the loop have been executed and it handles the closing of an API resource, like clearing a setTimeout or closing a stream event

const fs = require('fs')

setTimeout(() => console.log('Inside Timeout'))
const readableStream = fs.createReadStream(__filename);
readableStream.close();

// logs to the console last
readableStream.on('close', () => {
console.log('Closing read stream')
})

OUTPUT:
Inside Timeout
Closing read stream
  • Conclusion

Understanding the event loop, helps to write more performant Javascript code, especially when dealing with asynchronous tasks.

0
Subscribe to my newsletter

Read articles from Oluwatobi Akinola directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Oluwatobi Akinola
Oluwatobi Akinola