Demystify NodeJS Event Loop
programmingnodejs
Published at Feb 22, 2023
NodeJS is a single-threaded and non-blocking runtime environment, means, NodeJS can execute:
  • Only one task at a time
  • Multiple tasks at the same time.
  • How is it possible?
    Definitions
    Queue: A data structure that stores a list of elements, with two principal operations:
  • Enqueue: which adds an element to the end of the list
  • Dequeue: which removes the first element in the list.
  • Heap: A region of memory that is managed by the program. It is used to store objects and functions.
    Call Stack: A data structure that stores information about the called functions of a computer program.
    NodeJS APIs: A set of functions that are provided by NodeJS to interact with the operating system.
    Event Loop: A loop that checks the call stack and the queues of callback functions. It moves the callback functions from the queues to the call stack when the call stack is empty.
    Microtask Queue: A queue that stores the callback functions that are scheduled by the process.nextTick() and Promise APIs.
    Toolbox
    setImmediate(): Schedules a callback function to be executed after the current operation in the event loop is completed.
    setTimeout(): Schedules a callback function to be executed after a specified delay.
    process.nextTick(): Schedules a callback function to be executed before the next operation in the event loop.
    Promise: A JavaScript object that represents a value that may be available now, in the future, or never.
    Test
    Let's test your knowledge about the NodeJS event loop. What will be the output of the following code snippet? Please write down your answers before you run the code snippet.
    setImmediate(() => console.log('Immediate'))
    setTimeout(() => console.log('Timeout'), 0)
    
    Promise.resolve().then(() => {
      console.log('Promise 0')
      process.nextTick(() => console.log('Next tick 2'))
      Promise.resolve().then(() => console.log('Promise 2'))
    })
    
    Promise.resolve().then(() => {
      console.log('Promise 1')
      Promise.resolve().then(() => console.log('Promise 3'))
      process.nextTick(() => console.log('Next tick 3'))
    })
    
    process.nextTick(() => {
      console.log('Next tick 0')
      process.nextTick(() => console.log('Next tick 4'))
      Promise.resolve().then(() => console.log('Promise 4'))
    })
    
    process.nextTick(() => {
      console.log('Next tick 1')
      Promise.resolve().then(() => console.log('Promise 5'))
      process.nextTick(() => console.log('Next tick 5'))
    })
    
    console.log('Hello world!')
    Now can run the code or compare your answers with the output below. Is it suprising? If yes, you're ready to learn more about the event loop.
    Hello world!
    Next tick 0
    Next tick 1
    Next tick 4
    Next tick 5
    Promise 0
    Promise 1
    Promise 4
    Promise 5
    Promise 2
    Promise 3
    Next tick 2
    Next tick 3
    Timeout
    Immediate
    Flow
    Use Cases
    Point
    I believe, a solid understanding of the runtime environment is essential for building better products, regardless of the language or framework you are using.