Node 11.0.0将queueMicrotasks添加为实验性的。 doc说它类似于process.nextTick,但是队列由V8而不是Node.js管理。用queueMicrotasks代替process.nextTick的用例是什么?相互使用会提高性能吗?

最佳答案

Queue列

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

滴答滴答滴答滴答

  • microTasks
  • 计时器(已过期)
  • 立即

  • 如何检查队列?

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

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

    差异性

    这是相同的,两者的方式都是在执行当前函数或脚本之后立即执行任务。

    他们有不同的队列。 nextTick的队列由 Node 管理,微任务一个由v8管理。

    是什么意思?

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

    没有性能提升,区别在于执行函数/脚本之后将首先检查nextTick队列,并且必须将其考虑在内。
    如果您从不使用nextTick,而仅使用queueMicrotask,则您将具有仅使用nextTick的相同行为(考虑到您的任务将与另一个微任务一起放入队列中)

    用例可以是在执行任何微任务之前(例如,在promise then和/或catch之前)执行任务。值得注意的是,promise使用微任务,因此,添加到then/catch的任何回调都将添加到微任务队列中,并且当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:task5 | | | | task6
  • microTasks:task1 | | | | | | | | | | | | | | | task2 | task3 | task7
  • 计时器:task8
  • 立即:task4 | task9

  • 步骤1:执行nextTick队列中的所有任务
  • nextTick:空
  • microTasks:任务1 | task2 | task3 | task7
  • 计时器:task8
  • 立即:task4 | task9

  • 输出:
  • 勾号1
  • 勾号2

  • 步骤2:执行microTasks队列中的所有任务
  • nextTick:task10
  • microTasks:清空
  • 计时器:task8
  • 立即:task4 | task9

  • 输出:
  • 勾号1
  • 勾号2
  • 许诺1解决了
  • 许诺2解决了
  • 许诺3解决了
  • 微任务1

  • 步骤3:执行nextTick队列中的所有任务(通过执行微任务(task2)添加了一个新任务)
  • nextTick:空
  • microTasks:task11
  • 计时器:task8
  • 立即:task4 | task9

  • 输出:
  • 勾号1
  • 勾号2
  • 许诺1解决了
  • 许诺2解决了
  • 许诺3解决了
  • 微任务1
  • 勾号3

  • 步骤4:执行microTasks队列中的所有任务(执行task10会添加一个新任务)
  • nextTick:空
  • microTasks:清空
  • 计时器:task8
  • 立即:task4 | task9

  • 输出:
  • 勾号1
  • 勾号2
  • 许诺1解决了
  • 许诺2解决了
  • 许诺3解决了
  • 微任务1
  • 勾号3
  • 微任务2

  • 步骤5:nextTick和microTasks队列中没有更多任务,next execute timers队列中。
  • nextTick:空
  • microTasks:清空
  • 计时器:EMPTY
  • 立即:task4 | task9

  • 输出:
  • 勾号1
  • 勾号2
  • 许诺1解决了
  • 许诺2解决了
  • 许诺3解决了
  • 微任务1
  • 勾号3
  • 微任务2
  • 超时

  • 步骤6 :(过期的)计时器队列中没有更多任务,立即队列中执行任务
  • nextTick:空
  • microTasks:清空
  • 计时器:EMPTY
  • 立即发送:EMPTY

  • 输出:
  • 勾号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任务之后执行,要考虑到这一点对于避免错误很重要(如后面的示例所述)。

    关于node.js - process.nextTick和queueMicrotask之间的区别,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55467033/

    10-11 15:19