在上圖中,調(diào)用棧中遇到DOM操作、ajax請(qǐng)求以及setTimeout等WebAPIs的時(shí)候就會(huì)交給瀏覽器內(nèi)核的其他模塊進(jìn)行處理,webkit內(nèi)核在Javasctipt執(zhí)行引擎之外,有一個(gè)重要的模塊是webcore模塊。對(duì)于圖中WebAPIs提到的三種API,webcore分別提供了DOM Binding、network、timer模塊來(lái)處理底層實(shí)現(xiàn)。等到這些模塊處理完這些操作的時(shí)候?qū)⒒卣{(diào)函數(shù)放入任務(wù)隊(duì)列中,之后等棧中的task執(zhí)行完之后再去執(zhí)行任務(wù)隊(duì)列之中的回調(diào)函數(shù)。
從setTimeout看事件循環(huán)機(jī)制
下面用Philip Roberts的演講中的一個(gè)栗子來(lái)說(shuō)明事件循環(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é)
所有的代碼都要通過(guò)函數(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。
測(cè)試
for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(new Date, i); }, 1000); } console.log(new Date, i);
這段代碼是我從網(wǎng)上前不久的一篇文章80%應(yīng)聘者都不及格的 JS 面試題中找到的,現(xiàn)在我們就分析一下這段代碼究竟是怎么輸出最后文章中所說(shuō)的最后的執(zhí)行狀態(tài):
40% 的人會(huì)描述為:5 -> 5,5,5,5,5,即第 1 個(gè) 5 直接輸出,1 秒之后,輸出 5 個(gè) 5;
首先i=0時(shí),滿足條件,執(zhí)行棧執(zhí)行循環(huán)體里面的代碼,發(fā)現(xiàn)是setTimeout,將其出棧之后把延時(shí)執(zhí)行的函數(shù)交給Timer模塊進(jìn)行處理。
當(dāng)i=1,2,3,4時(shí),均滿足條件,情況和i=0時(shí)相同,因此timer模塊里面有5個(gè)相同的延時(shí)執(zhí)行的函數(shù)。
當(dāng)i=5的時(shí)候,不滿足條件,因此for循環(huán)結(jié)束,console.log(new Date, i)入棧,此時(shí)的i已經(jīng)變成了5。因此輸出5。
此時(shí)1s已經(jīng)過(guò)去,timer模塊將5個(gè)回調(diào)函數(shù)按照注冊(cè)的順序返回給任務(wù)隊(duì)列。
執(zhí)行引擎去執(zhí)行任務(wù)隊(duì)列中的函數(shù),5個(gè)function依次入棧執(zhí)行之后再出棧,此時(shí)的i已經(jīng)變成了5。因此幾乎同時(shí)輸出5個(gè)5。
因此等待的1s的時(shí)間其實(shí)只有輸出第一個(gè)5之后需要等待1s,這1s的時(shí)間是timer模塊需要等到的規(guī)定的1s時(shí)間之后才將回調(diào)函數(shù)交給任務(wù)隊(duì)列。等執(zhí)行棧執(zhí)行完畢之后再去執(zhí)行任務(wù)對(duì)列中的5個(gè)回調(diào)函數(shù)。這期間是不需要等待1s的。因此輸出的狀態(tài)就是:5 -> 5,5,5,5,5,即第1個(gè) 5 直接輸出,1s之后,輸出 5個(gè)5;
相關(guān)推薦:
js事件循環(huán)機(jī)制示例分析
以上就是javascript事件循環(huán)機(jī)制實(shí)例詳解的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!