0

I have seen many places that in nodeJs in micro task queue nextTick queue have more priority than promise queue but when I tried example of it in my device I didn't get expected output.But on online node compiler I got expected output.

process.nextTick(()=>console.log('In nextTick')) Promise.resolve().then(()=>console.log('In Promise'))

Expected Output: In nextTick In Promise Actual Output: In Promise In nextTick code and output

  • [Please do not upload images of code/data/errors.](//meta.stackoverflow.com/q/285551/1772220) – ugexe Apr 17 '23 at 15:21
  • You're certainly interpreting the script as ES-module on your device while the online interpreter would be interpreting it as CommonJS. The difference is that when loaded as ES-module, your script is actually already in a microtask, and thus queuing a resolved Promise will get it called immediately. You can see that even in CJS by doing something like `Promise.resolve().then(() => /* your code */)`. So the "priority" is actually not a priority, it depends on where in the even loop you are. The thing is that nextTick is before the microtasks relatively to the main task. – Kaiido Apr 17 '23 at 23:39
  • You are absolutely correct that I am interpreting the script as ES-module on my device but I am not getting what exactly you want to explain in this line so please can you elaborate it. "The difference is that when loaded as ES-module, your script is actually already in a microtask, and thus queuing a resolved Promise will get it called immediately." @Kaiido – Jeel Parikh Apr 18 '23 at 06:25

1 Answers1

0

The difference happens because on your device you are interpreting the script as an ES module (ESM), while the web REPL must be set to CommonJS (CJS).

In ESM, your script is ran in a global async context, so I guess (I still have to check the sources...) the event-loop is in the microtask phase. Queuing a microtask from the microtask phase will prevent the event-loop from leaving the microtask queue, and thus your callback will be executed right away.

You can see this by running this script as ESM:

const t1 = Date.now();
const fn = () => Promise.resolve().then(() => {
  if(Date.now()-t1 < 5000) {
    Promise.resolve().then(fn);
  }
  else {
    console.log("all promises done");
  }
});
process.nextTick(() => console.log("tick"));
fn();

This will block the script for 5s, then print "all promises done" followed by "tick".

And the same is true for the "tick phase" (not sure about the exact nomenclature):

const t1 = Date.now();
const fn = () => process.nextTick(() => {
  if(Date.now()-t1 < 5000) {
    process.nextTick(fn);
  }
  else {
    console.log("all ticks done");
  }
});
Promise.resolve().then(() => console.log("promise"));
fn();

So in CJS, this script will lock for 5s, then print "all ticks done" followed by "promise".

Note that even in browsers microtasks are blocking in the same way.


So we can't really say that one has higher priority than the other, what can be said however, is that from the "timeout phase" or "network phase", the "tick phase" will be entered first. And so a script like

setImmediate(() => {
  process.nextTick(()=>console.log('In nextTick'))
  Promise.resolve().then(()=>console.log('In Promise'))
});

will print "In nextTick" then "In Promise", whether the main script is interpreted as ESM or CJS.

Kaiido
  • 123,334
  • 13
  • 219
  • 285