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
一起执行。
最后,.then
把console.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
一起执行。
最后,.then
把console.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
一起执行。
最后,.then
把console.log(9);
放到Microtask Queue中。
这时候Macrotask Queue中已经没有task了,
这才开始从Microtask Queue中依次取出所有的task顺序执行。
此时,Microtask Queue中有以下两个task,
console.log(4);
和console.log(9);
。
于是,Node端就得到了以上输出。