Node 11.0.0 adds queueMicrotasks
as experimental. The doc says it is similar to process.nextTick
but the queue is managed by V8 instead of Node.js. What would be the use case to use queueMicrotasks
instead of process.nextTick
? Will there be any performance gain using one over another?
We can find different queues (show in check priority order after finish a function/script execution):
How the queues are checked?
First, nextTick queue is checked to get tasks for its execution, once it is exhausted, the microTasks queue is checked. After finishing the tasks in the microtask queue the process of checking nextTick and microTasks queues are repeated until the queues have been emptied.
The next queue to check is the timers one and in the end the immediate queue.
It is the same, in the way of both of them are to execute a task just after the execution of the current function or script.
They have different queues. The nextTick's queue is managed by node and the microtask one is managed by v8.
What is it means?
The nextTick queue is checked in first place after the current function/script execution, and then the microTask one.
There is no performance gain, the difference is that the nextTick queue will be checked first after the function/script execution and that must be taken into account. If you never use nextTick and only use queueMicrotask you will have the same behavior of just using nextTick (taking into account that your tasks will be placed in a queue with another microtasks)
The use case could be to execute tasks before any microtask, for example, before a promise then and/or catch. It worth noting that promises use microtask so, any callback added to then/catch will be added to the microtask queue and its execution will be performed when nextTick queue is empty.
After the execution of this code:
function task1() {
console.log('promise1 resolved');
}
function task2() {
console.log('promise2 resolved');
process.nextTick(task10);
}
function task3() {
console.log('promise3 resolved');
}
function task4() {
console.log('immediate 1');
}
function task5() {
console.log('tick 1');
}
function task6() {
console.log('tick 2');
}
function task7() {
console.log('microtask 1');
}
function task8() {
console.log('timeout');
}
function task9() {
console.log('immediate 2');
}
function task10() {
console.log('tick3');
queueMicrotask(task11);
}
function task11() {
console.log('microtask 2');
}
Promise.resolve().then(task1);
Promise.resolve().then(task2);
Promise.resolve().then(task3);
setImmediate(task4);
process.nextTick(task5);
process.nextTick(task6);
queueMicrotask(task7);
setTimeout(task8, 0);
setImmediate(task9);
Step 1: execute all tasks in nextTick queue
output:
Step 2: execute all tasks in microTasks queue
output:
Step 3: execute all tasks in nextTick queue (there is a new task added by the execution of microtask (task2))
output:
Step 4: execute all tasks in microTasks queue (there is a new task added by the execution of task10)
output:
Step 5: No more tasks in nextTick and microTasks queues, next execute timers queue.
output:
Step 6: No more tasks in (expired) timers queue, execute tasks in immediate queue
output:
As we can see there is no performance reason to choose one or another, the chosen decision depends on our needs and what needs to be done and when.
Imagine this code:
let i = 1;
queueMicrotask(() => console.log(i));
process.nextTick(() => i++);
The output will be 2 due to nextTick queue is first checked first.
but if you do
let i = 1;
queueMicrotask(() => console.log(i));
process.nextTick(() => queueMicrotask(() =>i++));
You will get 1.
With examples, I want to make you see that the uses cases come from your needs of what and when you need to perform a task. And the important thing is taking into account that then/catch callbacks in a promise are microtasks and will be executed after nextTick tasks, take into account this is important to avoid errors (as stated in the behind example).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With