When is process.nextTick() in the NodeJS event loop called?

Viewed 802

I have read the official documentation and the many other blogs on process.nextTick() but I am a little confused on certain topics.

Firstly, it is written that the callbacks in process.nextTick() are executed before the start of next iteration.

Secondly, when inside an I/O cycle, the event loop is currently in the poll phase. And after the poll phase the event-loop moves to the check phase where any callbacks called in setImmediate method are executed.

Now in the following code

const fs = require('fs');

function main() {
   fs.readFile('./xy.txt', function(err, buff){
      setTimeout(()=> console.log("timeout inside fs"),0);
      setImmediate(()=> console.log("immediate inside fs"));
      process.nextTick(()=>{console.log("process.nextTick")});
      console.log("inside fs");
   })
   console.log("called inside main fn in first-iteration");
}

main();
console.log("called in first-iteration);

The output would be:

called inside main fn in first-iteration
called in first iteration

During this the callback of the fs fn would be registered and the above two lines would be consoled as the entry script would be run without event-loop. And now the first-iteration of event loop will start, starting from the timer phase, as no timers are present it will move to the pending phase and then to the poll phase, where-in there is a callback (fs fn callback), the event-loop starting executing the fs callback and now the setTimeout callback and setImmediate callback would be registered. After that, the process.nextTick callback will be registered in the nextTickQueue which will be executed in the next iteration or tick (second iteration). Now after the poll phase ends, we have inside fs logged on the third line.

Next the event loop should move to check phase, where the setImmediate callback is present and should log immediate inside fs following that the event-loop should move on to the next-phases and the first iteration should end. Before starting the second iteration, process.nextTick should be logged and then timeout inside fs should be logged at last.

Thus making the final output to be:

called inside main fn in first-iteration
called in first iteration
inside fs
immediate inside fs
process.nextTick
timeout inside fs

But instead the output is coming out to be:

called inside main fn in first-iteration
called in first iteration
inside fs
process.nextTick
immediate inside fs
timeout inside fs

somewhere it was written that the microtasks (process.nextTick) are executed on top priorty and so the event loop will leave all its execution and execute the microtask at first glance. But if that too is the case, why is inside fs consoled before process.nextTick?

Basically, I am confused, how does process.nextTick() and the event-loop flow through phases go on?

1 Answers

One of the big misconceptions among node developers is that process.nextTick run code in the following tick(iteration)

Before answering your question let me tell you a few things:

enter image description here

process.nextTick() is not technically part of the event loop. Instead, the nextTickQueue will be processed after the current operation is completed, regardless of the current phase of the event loop.Next tick queue is displayed separately from the other four main queues because it is not natively provided by the libuv, but implemented in Node

Before each phase of the event loop (timers queue, IO events queue, immediates queue,close handlers queue are the four main phases), before moving to the phase, Node checks for the nextTick queue for any queued events. If the queue is not empty, Node will start processing the queue immediately until the queue is empty, before moving to the event loop next phase.

  1. There are some changes introduced in Node v11 which significantly changes the execution order of nextTick, Promise callbacks, setImmediate and setTimeout callbacks since Node v11.

  2. nextTikcs and promise callback queues are processed between each timer and immediate callback no matter if othere timeouts callback and immidate callback already present in corresponding queue.

  3. Run this https://repl.it/@sandeepp2016/processNextTick#index.js in before nodejs11 and nodejs11 and above you would see the difference in the execution order.

You almost understood the flow correctly except when the nextTick gets called and how synchronous and aysnchronous code is executed in the Nodejs.

When event loop enters in poll phase it will execute fs.read callback queued to io events queue. Since there are no further io events callbacks are in the queue, the event loop will enter to the check phase but before running callbacks of the check phase, it will execute all nexttikcs and promise tasks queue. That's why process.nexttick got printed in your case and then immediate inside fs After the check phase it will wrap back to the timer phase and print timeout inside fs.

To your last question, When the program enters in main function first three calls are asynchronous, the first two will be queued to the event loop. But console.log("inside fs") will immediately be put to call stack and executed by Nodejs main tread. process.next also will be executed asynchronously(Not a part of libuv event loop) but after the poll phase and before the check phase as depicted in the image.Hence it will print inside fs before process.nextTick.

Keep in mind that only asynchronous code is handled by the event loop, synchronous code is directly executed in the main thread without the event loop.

For more details read this out

https://blog.insiderattack.net/promises-next-ticks-and-immediates-nodejs-event-loop-part-3-9226cbe7a6aa

https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

Related