淺薄概念
Javascript是單線程,執(zhí)行任務(wù)時(shí),分同步任務(wù)和異步任務(wù),執(zhí)行同步任務(wù)時(shí)放入棧中執(zhí)行,執(zhí)行異步任務(wù)時(shí)由瀏覽器放入異步隊(duì)列,等同步任務(wù)執(zhí)行完后,再去異步隊(duì)列詢問是否有可執(zhí)行的回調(diào)函數(shù),一直循環(huán)直到異步隊(duì)列清空。
沒有深入!
深入理解
函數(shù)調(diào)用棧和任務(wù)隊(duì)列
JavaScript有一個(gè)main thread主進(jìn)程和call-stack(一個(gè)調(diào)用堆棧),在對
一個(gè)調(diào)用堆棧中的task處理的時(shí)候,其他的都要等著。當(dāng)在執(zhí)行過程中遇到一些類似于setTimeout等異步操作的時(shí)候,會(huì)交給瀏覽器的其他模塊(以webkit為例,是webcore模塊)進(jìn)行處理,當(dāng)?shù)竭_(dá)setTimeout指定的延時(shí)執(zhí)行的時(shí)間后,task(回調(diào)函數(shù))會(huì)放入到任務(wù)隊(duì)列直至,一般不同的異步任務(wù)的的回調(diào)函數(shù)會(huì)放入不同的任務(wù)隊(duì)列之中。等到調(diào)用棧中所有task執(zhí)行完畢之后,接著去執(zhí)行任務(wù)隊(duì)列中的task(回調(diào)函數(shù))

在上圖中,調(diào)用棧中遇到DOM操作、ajax請求以及setTimeout等WebAPIs的時(shí)候就會(huì)交給瀏覽器內(nèi)核的其他模塊進(jìn)行處理,webkit內(nèi)核在Javasctipt執(zhí)行引擎之外,有一個(gè)重要的模塊是webcore模塊。對于圖中WebAPIs提到的三種API,webcore分別提供了DOM Binding、network、timer模塊來處理底層實(shí)現(xiàn)。等到這些模塊處理完這些操作的時(shí)候?qū)⒒卣{(diào)函數(shù)放入任務(wù)隊(duì)列中,之后等棧中的task執(zhí)行完之后再去執(zhí)行任務(wù)隊(duì)列之中的回調(diào)函數(shù)。
從setTimeout看事件循環(huán)機(jī)制
一個(gè)例子來說明事件循環(huán)機(jī)制究竟是怎么執(zhí)行setTimeout的。

首先main()函數(shù)的執(zhí)行上下文入棧,

代碼接著執(zhí)行,遇到console.log(‘Hi’),此時(shí)log(‘Hi’)入棧,console.log方法只是一個(gè)webkit內(nèi)核支持的普通的方法,所以log(‘Hi’)方法立即被執(zhí)行。此時(shí)輸出’Hi’。

當(dāng)遇到setTimeout的時(shí)候,執(zhí)行引擎將其添加到棧中。

調(diào)用棧發(fā)現(xiàn)setTimeout是之前提到的WebAPIs中的API,因此將其出棧之后將延時(shí)執(zhí)行的函數(shù)交給瀏覽器的timer模塊進(jìn)行處理。

timer模塊去處理延時(shí)執(zhí)行的函數(shù),此時(shí)執(zhí)行引擎接著執(zhí)行將log(‘SJS’)添加到棧中,此時(shí)輸出’SJS’。

當(dāng)timer模塊中延時(shí)方法規(guī)定的時(shí)間到了之后就將其放入到任務(wù)隊(duì)列之中,此時(shí)調(diào)用棧中的task已經(jīng)全部執(zhí)行完畢。


調(diào)用棧中的task執(zhí)行完畢之后,執(zhí)行引擎會(huì)接著看執(zhí)行任務(wù)隊(duì)列中是否有需要執(zhí)行的回調(diào)函數(shù)。這里的cb函數(shù)被執(zhí)行引擎添加到調(diào)用棧中,接著執(zhí)行里面的代碼,輸出’there’。等到執(zhí)行結(jié)束之后再出棧。
小結(jié)
上面的這一個(gè)流程解釋了當(dāng)瀏覽器遇到setTimeout之后究竟是怎么執(zhí)行的,相類似的還有前面圖中提到的另外的API以及另外一些異步的操作。
總結(jié)上文說的,主要就是以下幾點(diǎn):
所有的代碼都要通過函數(shù)調(diào)用棧中調(diào)用執(zhí)行。
當(dāng)遇到前文中提到的APIs的時(shí)候,會(huì)交給瀏覽器內(nèi)核的其他模塊進(jìn)行處理。
任務(wù)隊(duì)列中存放的是回調(diào)函數(shù)。
等到調(diào)用棧中的task執(zhí)行完之后再回去執(zhí)行任務(wù)隊(duì)列之中的task。
問題
(function test() {
setTimeout(function() {console.log(4)}, 0);
new Promise(function executor(resolve) {
console.log(1);
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(2);
}).then(function() {
console.log(5);
});
console.log(3);
})()
在這段代碼里面,多了一個(gè)promise,那么我們可以思考下面這個(gè)問題:
promise的task會(huì)放在不同的任務(wù)隊(duì)列里面,那么setTimeout的任務(wù)隊(duì)列和promise的任務(wù)隊(duì)列的執(zhí)行順序又是怎么的呢?
到這里大家看了我說了這么多的task,那么上文中一直提到的task究竟包括了什么?具體是怎么分的?
原文:https://zhuanlan.zhihu.com/p/26229293