There's an important semantic with microtasks and nextTicks which depends on the Node version.
Before Node v11, nextTick queue was executed between each phase of the event loop (timers, I/O, immediates, close handlers are the four phases). Therefore, before Node v11, promise callbacks were also executed between each phase in the event loop. (I've written about this in detail here: https://blog.insiderattack.net/promises-next-ticks-and-immediates-nodejs-event-loop-part-3-9226cbe7a6aa)
However, starting from Node v11, event loop jumps to microtask queue whenever a microtask is added to the microtask queue as part of the execution of the program. You can experiment this with the following snippet. The same applies to nextTick queue too. You can read more here: https://blog.insiderattack.net/new-changes-to-timers-and-microtasks-from-node-v11-0-0-and-above-68d112743eb3
setImmediate(() => console.log('timeout1'));
setImmediate(() => {
console.log('timeout2')
Promise.resolve().then(() => console.log('promise'))
});
setImmediate(() => console.log('timeout3'));
setImmediate(() => console.log('timeout4'));
The output of the above code changes depending on the Node.js version as follows:
$ node -v
v10.19.0
$ node test.js
timeout1
timeout2
timeout3
timeout4
promise
$ nvm use 11
Now using node v11.15.0 (npm v6.7.0)
$ node test.js
timeout1
timeout2
promise
timeout3
timeout4
Therefore it's important to know that: nextTicks and microtasks have an even higher priority in Node versions >=11, because they get the chance to be processed within the current phase of the event loop. But in earlier Node versions, nextTicks and microtasks are executed at the end of each phase of the loop.
On a side note, it's important to know that microtasks queue is a part of v8 engine and not maintained in Node.js runtime. However, Node.js event loop instructs v8 to run all microtasks, once Node.js finishes with the nextTick queue. Therefore, promise callbacks are executed after nextTick queue.