[Node] Macrotask和Microtask Queue在Node端的不同表现

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端就得到了以上输出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。