1. 背景
我們知道萤捆,v8在處理setTimeout和Promise的時候,
會區(qū)分Macrotask Queue和Microtask Queue栈幸。
setTimeout會將task放到Macrotask Queue中击费,
而Promise會將task放到Microtask Queue中。
一開始執(zhí)行的代碼在Macrotask Queue中楞遏。
v8在執(zhí)行代碼的時候茬暇,
(1)首先從Macrotask Queue中取出一個task執(zhí)行首昔,
(2)執(zhí)行完后,再從Microtask Queue中依次取出所有的task順序執(zhí)行糙俗,
(3)等這些Microtask Queue中的task都執(zhí)行完勒奇,再進(jìn)行第(1)步開始循環(huán)執(zhí)行。
2. 場景
以上執(zhí)行過程在瀏覽器環(huán)境中是沒有問題的巧骚,
但是在Node環(huán)境中赊颠,第(1)條卻有些出入。
在Node環(huán)境中劈彪,
從Macrotask Queue中竣蹦,也是依次取出所有的task順序執(zhí)行,
而不是只取出一個task執(zhí)行沧奴。
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 瀏覽器環(huán)境
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
在瀏覽器環(huán)境中痘括,一開始所有代碼的都在Macrotask Queue中,
取出來開始執(zhí)行滔吠,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
立即執(zhí)行冬骚,
而.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)步,執(zhí)行完畢属愤。
然后按著v8執(zhí)行流程女器,開始第(2)步,
從Microtask Queue中依次取出所有的task順序執(zhí)行住诸。
目前Microtask Queue中只有console.log(6);
驾胆,執(zhí)行完畢涣澡。
接著下一輪循環(huán),又重新開始第(1)步丧诺,
從Macrotask Queue中取出一個task執(zhí)行入桂,將取出以下task,
console.log(2);
new Promise((res, rej) => {
console.log(3);
res();
}).then(() => {
console.log(4);
});
于是驳阎,先執(zhí)行console.log(2);
抗愁,
然后console.log(3);
跟著new Promise
一起執(zhí)行。
最后呵晚,.then
把console.log(4);
放到Microtask Queue中蜘腌。
分歧點(diǎn):====> ====> ====> ====> ====> ====> ====> ====>
下面就出現(xiàn)了瀏覽器環(huán)境和Node環(huán)境的分歧了。
這時候根據(jù)v8執(zhí)行流程第(1)步饵隙,已經(jīng)執(zhí)行了一個task撮珠,
該從Microtask Queue中依次取出所有的task順序執(zhí)行了。
瀏覽器環(huán)境中金矛,確實是這么執(zhí)行的芯急,
這時候Microtask Queue中只有console.log(4);
,執(zhí)行它驶俊。
然后就下一輪循環(huán)了娶耍,重新執(zhí)行第(1)步,
從Macrotask Queue中取出一個task執(zhí)行饼酿,
將取出以下task榕酒,
console.log(7);
new Promise((res, rej) => {
console.log(8);
res();
}).then(() => {
console.log(9);
});
先執(zhí)行console.log(7);
,
然后console.log(8);
跟著new Promise
一起執(zhí)行嗜湃。
最后奈应,.then
把console.log(9);
放到Microtask Queue中。
Macrotask Queue中的這個task執(zhí)行完了购披,該執(zhí)行第(2)步了杖挣,
從Microtask Queue中依次取出所有的task順序執(zhí)行,
這時候Microtask Queue中只有console.log(9);
刚陡,執(zhí)行它惩妇。
于是,瀏覽器端就得到了以上輸出筐乳。
2.3 Node環(huán)境
v 6.12.0
v 8.9.1
1
5
6
2
3
7
8
4
9
Node環(huán)境中歌殃,以上執(zhí)行過程,在分歧點(diǎn)之前都是一樣的蝙云。
在Node環(huán)境中氓皱,并不會一次只從Macrotask Queue中取出一個task執(zhí)行,
而是和Microtask Queue一樣,取出所有的task依次執(zhí)行波材。
因此股淡,在分歧點(diǎn)處,
一個Macrotask Queue中的task執(zhí)行完后廷区,還會再次取task唯灵,得到了,
console.log(7);
new Promise((res, rej) => {
console.log(8);
res();
}).then(() => {
console.log(9);
});
先執(zhí)行console.log(7);
隙轻,
然后console.log(8);
跟著new Promise
一起執(zhí)行埠帕。
最后,.then
把console.log(9);
放到Microtask Queue中玖绿。
這時候Macrotask Queue中已經(jīng)沒有task了敛瓷,
這才開始從Microtask Queue中依次取出所有的task順序執(zhí)行。
此時镰矿,Microtask Queue中有以下兩個task琐驴,
console.log(4);
和console.log(9);
俘种。
于是秤标,Node端就得到了以上輸出。