process.nextTick 和 queueMicrotask 的区别

Posted

技术标签:

【中文标题】process.nextTick 和 queueMicrotask 的区别【英文标题】:Difference between process.nextTick and queueMicrotask 【发布时间】:2019-08-23 07:44:36 【问题描述】:

节点 11.0.0 添加 queueMicrotasks 作为实验。 doc 表示它类似于 process.nextTick,但队列由 V8 而不是 Node.js 管理。使用queueMicrotasks 而不是process.nextTick 的用例是什么?使用一个比另一个有任何性能提升吗?

【问题讨论】:

重复***.com/questions/25915634/… @VikashSingh 这不是重复的。我在问之前检查了那个问题。它解释了微任务和宏任务之间的区别。但根据接受的答案,process.nextTick 也属于微任务列表 【参考方案1】:

队列

我们可以找到不同的队列(在完成函数/脚本执行后以检查优先级顺序显示):

nextTick 微任务 计时器(已过期) 立即

如何检查队列?

首先,检查 nextTick 队列以获取执行任务,一旦耗尽,检查 microTasks 队列。完成微任务队列中的任务后检查的过程 重复 nextTick 和 microTasks 队列,直到队列被清空。

下一个要检查的队列是定时器一,最后是立即队列。

区别

都是一样的,方式都是在当前函数或脚本执行完之后再执行一个任务。

他们有不同的队列。 nextTick 的队列由 node 管理,microtask one 由 v8 管理。

是什么意思?

在当前函数/脚本执行之后首先检查 nextTick 队列,然后是 microTask 队列。

没有性能提升,不同的是在函数/脚本执行后将首先检查 nextTick 队列,必须考虑到这一点。 如果您从不使用 nextTick 而只使用 queueMicrotask,您将获得与仅使用 nextTick 相同的行为(考虑到您的任务将与其他微任务一起放入队列中)

用例可能是在任何微任务之前执行任务,例如,在承诺 then 和/或 catch 之前。值得注意的是,promise 使用的是 microtask,所以任何添加到 then/catch 的回调都会被添加到 microtask 队列中,并在 nextTick 队列为空时执行。

示例

这段代码执行后:

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);

执行

nextTick:任务5 |任务6 微任务:task1 |任务2 |任务3 |任务7 定时器:task8 立即:任务4 |任务9

第一步:执行 nextTick 队列中的所有任务

nextTick:空 微任务:task1 |任务2 |任务3 |任务7 定时器:task8 立即:任务4 |任务9

输出:

勾选 1 勾选 2

第 2 步:执行 microTasks 队列中的所有任务

nextTick: task10 微任务:空 定时器:task8 立即:任务4 |任务9

输出:

勾选 1 勾选 2 承诺 1 已解决 承诺 2 已解决 承诺 3 已解决 微任务 1

第三步:执行nextTick队列中的所有任务(微任务(task2)的执行增加了一个新任务)

nextTick:空 微任务:task11 定时器:task8 立即:任务4 |任务9

输出:

勾选 1 勾选 2 承诺 1 已解决 承诺 2 已解决 承诺 3 已解决 微任务 1 勾选 3

第四步:执行microTasks队列中的所有任务(task10的执行增加了一个新任务)

nextTick:空 微任务:空 定时器:task8 立即:任务4 |任务9

输出:

勾选 1 勾选 2 承诺 1 已解决 承诺 2 已解决 承诺 3 已解决 微任务 1 勾选 3 微任务 2

第 5 步:nextTick 和 microTasks 队列中不再有任务,下一个执行计时器队列。

nextTick:空 微任务:空 定时器:空 立即:任务4 |任务9

输出:

勾选 1 勾选 2 承诺 1 已解决 承诺 2 已解决 承诺 3 已解决 微任务 1 勾选 3 微任务 2 超时

第 6 步:(过期)计时器队列中没有更多任务,执行立即队列中的任务

nextTick:空 微任务:空 定时器:空 立即:空

输出:

勾选 1 勾选 2 承诺 1 已解决 承诺 2 已解决 承诺 3 已解决 微任务 1 勾选 3 微任务 2 超时 立即 1 立即2

正如我们所见,选择一个或另一个没有性能原因,所选择的决定取决于我们的需求以及需要做什么以及何时完成。

想象一下这段代码:

let i = 1;

queueMicrotask(() => console.log(i));
process.nextTick(() => i++);

由于首先检查 nextTick 队列,因此输出将为 2。

如果你这样做了

let i = 1;

queueMicrotask(() => console.log(i));
process.nextTick(() => queueMicrotask(() =>i++));

你会得到 1 个。

通过示例,我想让您了解用例来自您对执行任务的内容和时间的需求。重要的是要考虑到 promise 中的 then/catch 回调是微任务,将在 nextTick 任务之后执行,考虑到这对于避免错误很重要(如后面的示例所述)。

【讨论】:

我在这些单独的 nextTick 和 microTasks 队列中看到的一个有趣的用例是安排一个任务在 DataLoader 中的所有微任务之后立即运行 github.com/graphql/dataloader/blob/… 请注意,与超时相关的即时时间似乎是不确定的。在微任务、nextTick 作业或即时任务中设置一些零超时和一些立即数似乎在超时之前、之后或中间执行所有立即数(作为一个块) 实际上上下文无关紧要,多次将其作为脚本运行:let msg = []; for(let i = 0; i < 20; i++) setImmediate(()=>msg.push('i'+i)); setTimeout(()=>msg.push('T'+i)); process.on('exit',()=>console.log(...msg)); 已经为我展示了这种现象(多核 x86_64 上 linux 上的节点 v12.18.2)跨度> 解释正确。但是,您能否对这个图表有所了解:nodejs.org/en/docs/guides/event-loop-timers-and-nexttick?这似乎与您的解释不符。 @Praveen 是对的。您的回答非常出色,但 Node.js 官方文档上的图表将计时器放在了顶部。你能解释一下吗?

以上是关于process.nextTick 和 queueMicrotask 的区别的主要内容,如果未能解决你的问题,请参考以下文章

译Node.js 事件循环, 定时器, 和 process.nextTick()

Node.js 中 process.nextTick 的正确用例是啥?

process.nextTick(function() throw err; ) 错误:getaddrinfo ENOTFOUND 节点节点:27017 - nodejs

译Node.js的eventloop,timers和process.nextTick()

process.nextTick,Promise.then,setTimeout,setImmediate执行顺序

开玩笑:测试不能在 setImmediate 或 process.nextTick 回调中失败