美文网首页Node.js
[Node] Macrotask和Microtask Queue

[Node] Macrotask和Microtask Queue

作者: 何幻 | 来源:发表于2017-12-08 21:02 被阅读118次

1. 背景

我们知道,v8在处理setTimeout和Promise的时候,
会区分Macrotask Queue和Microtask Queue。

setTimeout会将task放到Macrotask Queue中,
而Promise会将task放到Microtask Queue中。

一开始执行的代码在Macrotask Queue中。

v8在执行代码的时候,
(1)首先从Macrotask Queue中取出一个task执行
(2)执行完后,再从Microtask Queue中依次取出所有的task顺序执行
(3)等这些Microtask Queue中的task都执行完,再进行第(1)步开始循环执行。

2. 场景

以上执行过程在浏览器环境中是没有问题的,
但是在Node环境中,第(1)条却有些出入。

在Node环境中,
从Macrotask Queue中,也是依次取出所有的task顺序执行
而不是只取出一个task执行

2.1 示例代码

console.log(1);

setTimeout(() => {
    console.log(2);
    new Promise((res, rej) => {
        console.log(3);
        res();
    }).then(() => {
        console.log(4);
    });
}, 0);

new Promise((res, rej) => {
    console.log(5);
    res();
}).then(() => {
    console.log(6);
});

setTimeout(() => {
    console.log(7);
    new Promise((res, rej) => {
        console.log(8);
        res();
    }).then(() => {
        console.log(9);
    });
}, 0);

2.2 浏览器环境

Chrome 62.0.3202.94(正式版本)(64 位)
Safari 10.0.3 (12602.4.8)
Firefox 57.0.2 (64-bit)

1
5
6
2
3
4
7
8
9

在浏览器环境中,一开始所有代码的都在Macrotask Queue中,
取出来开始执行,console.log(1);
紧接着,遇到setTimeout,于是将以下task放到Macrotask Queue中,

console.log(2);
new Promise((res, rej) => {
    console.log(3);
    res();
}).then(() => {
    console.log(4);
});

随后,console.log(5);跟着new Promise立即执行,
.then则将console.log(6);放到了Microtask Queue中。

最后,后面的第二个setTimeout,又将以下task放到了Macrotask Queue中,

console.log(7);
new Promise((res, rej) => {
    console.log(8);
    res();
}).then(() => {
    console.log(9);
});

第(1)步,执行完毕。

然后按着v8执行流程,开始第(2)步,
从Microtask Queue中依次取出所有的task顺序执行。
目前Microtask Queue中只有console.log(6);,执行完毕。

接着下一轮循环,又重新开始第(1)步,
从Macrotask Queue中取出一个task执行,将取出以下task,

console.log(2);
new Promise((res, rej) => {
    console.log(3);
    res();
}).then(() => {
    console.log(4);
});

于是,先执行console.log(2);
然后console.log(3);跟着new Promise一起执行。
最后,.thenconsole.log(4);放到Microtask Queue中。

分歧点:====> ====> ====> ====> ====> ====> ====> ====>
下面就出现了浏览器环境和Node环境的分歧了。

这时候根据v8执行流程第(1)步,已经执行了一个task,
该从Microtask Queue中依次取出所有的task顺序执行了。

浏览器环境中,确实是这么执行的,
这时候Microtask Queue中只有console.log(4);,执行它。

然后就下一轮循环了,重新执行第(1)步,
从Macrotask Queue中取出一个task执行,
将取出以下task,

console.log(7);
new Promise((res, rej) => {
    console.log(8);
    res();
}).then(() => {
    console.log(9);
});

先执行console.log(7);
然后console.log(8);跟着new Promise一起执行。
最后,.thenconsole.log(9);放到Microtask Queue中。

Macrotask Queue中的这个task执行完了,该执行第(2)步了,
从Microtask Queue中依次取出所有的task顺序执行,
这时候Microtask Queue中只有console.log(9);,执行它。

于是,浏览器端就得到了以上输出。

2.3 Node环境

v 6.12.0
v 8.9.1

1
5
6
2
3
7
8
4
9

Node环境中,以上执行过程,在分歧点之前都是一样的。
在Node环境中,并不会一次只从Macrotask Queue中取出一个task执行,
而是和Microtask Queue一样,取出所有的task依次执行

因此,在分歧点处,
一个Macrotask Queue中的task执行完后,还会再次取task,得到了,

console.log(7);
new Promise((res, rej) => {
    console.log(8);
    res();
}).then(() => {
    console.log(9);
});

先执行console.log(7);
然后console.log(8);跟着new Promise一起执行。
最后,.thenconsole.log(9);放到Microtask Queue中。

这时候Macrotask Queue中已经没有task了,
这才开始从Microtask Queue中依次取出所有的task顺序执行。
此时,Microtask Queue中有以下两个task,
console.log(4);console.log(9);

于是,Node端就得到了以上输出。

相关文章

网友评论

    本文标题:[Node] Macrotask和Microtask Queue

    本文链接:https://www.haomeiwen.com/subject/qtjyixtx.html