深入淺出Javascript事件循環(huán)機(jī)制-上(轉(zhuǎn))

淺薄概念

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ù))

image.png

在上圖中,調(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的。

image.png

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

image.png

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

image.png

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

image.png

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


image.png

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

image.png

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

image.png
image.png

調(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容