2020-9-2 seo達(dá)人
在開(kāi)始正文前,我們先把本文涉及到的一些內(nèi)容提前定個(gè)基調(diào)。
Promise 中只有涉及到狀態(tài)變更后才需要被執(zhí)行的回調(diào)才算是微任務(wù),比如說(shuō) then
、 catch
、finally
,其他所有的代碼執(zhí)行都是宏任務(wù)(同步執(zhí)行)。
上圖中藍(lán)色為同步執(zhí)行,黃色為異步執(zhí)行(丟到微任務(wù)隊(duì)列中)。
這個(gè)問(wèn)題我們根據(jù) ecma 規(guī)范來(lái)看:
[[PromiseFulfillReactions]]
和 [[PromiseRejectReactions]]
中。如果你看過(guò)手寫(xiě) Promise 的代碼的話,應(yīng)該能發(fā)現(xiàn)有兩個(gè)數(shù)組存儲(chǔ)這些回調(diào)函數(shù)。
了解完以上知識(shí)后,正片開(kāi)始。
Promise.resolve()
.then(() => { console.log("then1"); Promise.resolve().then(() => { console.log("then1-1");
});
})
.then(() => { console.log("then2");
});
以上代碼大家應(yīng)該都能得出正確的答案:then1 → then1-1 → then2
。
雖然 then
是同步執(zhí)行,并且狀態(tài)也已經(jīng)變更。但這并不代表每次遇到 then
時(shí)我們都需要把它的回調(diào)丟入微任務(wù)隊(duì)列中,而是等待 then
的回調(diào)執(zhí)行完畢后再根據(jù)情況執(zhí)行對(duì)應(yīng)操作。
基于此,我們可以得出第一個(gè)結(jié)論:鏈?zhǔn)秸{(diào)用中,只有前一個(gè) then
的回調(diào)執(zhí)行完畢后,跟著的 then
中的回調(diào)才會(huì)被加入至微任務(wù)隊(duì)列。
大家都知道了 Promise resolve
后,跟著的 then
中的回調(diào)會(huì)馬上進(jìn)入微任務(wù)隊(duì)列。
那么以下代碼你認(rèn)為的輸出會(huì)是什么?
let p = Promise.resolve();
p.then(() => { console.log("then1"); Promise.resolve().then(() => { console.log("then1-1");
});
}).then(() => { console.log("then1-2");
});
p.then(() => { console.log("then2");
});
按照一開(kāi)始的認(rèn)知我們不難得出 then2
會(huì)在 then1-1
后輸出,但是實(shí)際情況卻是相反的。
基于此我們得出第二個(gè)結(jié)論:每個(gè)鏈?zhǔn)秸{(diào)用的開(kāi)端會(huì)首先依次進(jìn)入微任務(wù)隊(duì)列。
接下來(lái)我們換個(gè)寫(xiě)法:
let p = Promise.resolve().then(() => { console.log("then1"); Promise.resolve().then(() => { console.log("then1-1");
});
}).then(() => { console.log("then2");
});
p.then(() => { console.log("then3");
});
上述代碼其實(shí)有個(gè)陷阱,then
每次都會(huì)返回一個(gè)新的 Promise,此時(shí)的 p
已經(jīng)不是 Promise.resolve()
生成的,而是最后一個(gè) then
生成的,因此 then3
應(yīng)該是在 then2
后打印出來(lái)的。
順便我們也可以把之前得出的結(jié)論優(yōu)化為:同一個(gè) Promise 的每個(gè)鏈?zhǔn)秸{(diào)用的開(kāi)端會(huì)首先依次進(jìn)入微任務(wù)隊(duì)列。
以下大家可以猜猜 then1-2
會(huì)在何時(shí)打印出來(lái)?
Promise.resolve()
.then(() => { console.log("then1"); Promise.resolve()
.then(() => { console.log("then1-1"); return 1;
})
.then(() => { console.log("then1-2");
});
})
.then(() => { console.log("then2");
})
.then(() => { console.log("then3");
})
.then(() => { console.log("then4");
});
這題肯定是簡(jiǎn)單的,記住第一個(gè)結(jié)論就能得出答案,以下是解析:
resolve
后第一個(gè) then
的回調(diào)進(jìn)入微任務(wù)隊(duì)列并執(zhí)行,打印 then1
resolve
后內(nèi)部第一個(gè) then
的回調(diào)進(jìn)入微任務(wù)隊(duì)列,此時(shí)外部第一個(gè) then
的回調(diào)全部執(zhí)行完畢,需要將外部的第二個(gè) then
回調(diào)也插入微任務(wù)隊(duì)列。
then1-1
和 then2
,然后分別再將之后 then
中的回調(diào)插入微任務(wù)隊(duì)列
then1-2
和 then3
,之后的內(nèi)容就不一一說(shuō)明了
接下來(lái)我們把 return 1
修改一下,結(jié)果可就大不相同啦:
Promise.resolve()
.then(() => { console.log("then1"); Promise.resolve()
.then(() => { console.log("then1-1"); return Promise.resolve();
})
.then(() => { console.log("then1-2");
});
})
.then(() => { console.log("then2");
})
.then(() => { console.log("then3");
})
.then(() => { console.log("then4");
});
當(dāng)我們 return Promise.resolve()
時(shí),你猜猜 then1-2
會(huì)何時(shí)打印了?
答案是最后一個(gè)才被打印出來(lái)。
為什么在 then
中分別 return
不同的東西,微任務(wù)的執(zhí)行順序竟有如此大的變化?以下是筆者的解析。
PS:then
返回一個(gè)新的 Promise,并且會(huì)用這個(gè) Promise 去 resolve
返回值,這個(gè)概念需要大家先了解一下。
根據(jù)規(guī)范 2.3.2,如果 resolve
了一個(gè) Promise,需要為其加上一個(gè) then
并 resolve
。
if (x instanceof MyPromise) { if (x.currentState === PENDING) {
} else {
x.then(resolve, reject);
} return;
}
上述代碼節(jié)選自手寫(xiě) Promise 實(shí)現(xiàn)。
那么根據(jù) A+ 規(guī)范來(lái)說(shuō),如果我們?cè)?nbsp;then
中返回了 Promise.resolve
的話會(huì)多入隊(duì)一次微任務(wù),但是這個(gè)結(jié)論還是與實(shí)際不符的,因此我們還需要尋找其他權(quán)威的文檔。
根據(jù)規(guī)范 25.6.1.3.2,當(dāng) Promise resolve
了一個(gè) Promise 時(shí),會(huì)產(chǎn)生一個(gè)NewPromiseResolveThenableJob,這是屬于 Promise Jobs 中的一種,也就是微任務(wù)。
This Job uses the supplied thenable and its then method to resolve the given promise. This process must take place as a Job to ensure that the evaluation of the then method occurs after evaluation of any surrounding code has completed.
并且該 Jobs 還會(huì)調(diào)用一次 then
函數(shù)來(lái) resolve Promise
,這也就又生成了一次微任務(wù)。
這就是為什么會(huì)觸發(fā)兩次微任務(wù)的來(lái)源。
藍(lán)藍(lán)設(shè)計(jì)( m.paul-jarrel.com )是一家專注而深入的界面設(shè)計(jì)公司,為期望卓越的國(guó)內(nèi)外企業(yè)提供卓越的UI界面設(shè)計(jì)、BS界面設(shè)計(jì) 、 cs界面設(shè)計(jì) 、 ipad界面設(shè)計(jì) 、 包裝設(shè)計(jì) 、 圖標(biāo)定制 、 用戶體驗(yàn) 、交互設(shè)計(jì)、 網(wǎng)站建設(shè) 、平面設(shè)計(jì)服務(wù)
藍(lán)藍(lán)設(shè)計(jì)的小編 http://m.paul-jarrel.com