队列
我们可以找到不同的队列(在完成函数/脚本执行后以检查优先级顺序显示):
如何检查队列?
首先,检查 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
输出:
第 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 任务之后执行,考虑到这对于避免错误很重要(如后面的示例所述)。