Javascript基礎(chǔ)系列之事件循環(huán)

事件循環(huán)(Event Loop)

規(guī)范中定義Event Loop如下

To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section. There are two kinds of event loops: those for browsing contexts, and those for workers

為了協(xié)調(diào)事件、用戶交互、腳本、UI渲染、網(wǎng)絡(luò)請(qǐng)求等行為,用戶引擎必須使用Event Loop。有兩種Event Loop,一種是基于browsing contexts,另外一種是基于workers

Philip Roberts的演講中Help, I’m stuck in an event-loop中一張圖表示如下

EC_04.jpg

調(diào)用棧遇見DOM操作、ajax請(qǐng)求以及setTimout等WebAPIs的時(shí)候就會(huì)交給瀏覽器內(nèi)核的其他模塊進(jìn)行處理,webkit內(nèi)核在javascript執(zhí)行引擎以外,有一個(gè)重要模塊是webcore模塊。對(duì)于圖中WebAPIs提到的三種API,webcore分別提供了DOM binding、network、timer模塊來底層實(shí)現(xiàn) 。等到這些模塊處理玩這些操作的時(shí)候?qū)⒒卣{(diào)函數(shù)任務(wù)異步隊(duì)列中,之后等棧中的task執(zhí)行完以后再去執(zhí)行任務(wù)隊(duì)列中的回掉函數(shù)

EC_03.jpg
  • 同步的任務(wù)直接進(jìn)入主執(zhí)行棧(call stack)中執(zhí)行
  • 等待主執(zhí)行棧中任務(wù)執(zhí)行完畢,由Event Loop將任務(wù)推入主執(zhí)行棧執(zhí)行

task

一個(gè)事件循環(huán)可以有多個(gè)task隊(duì)列

來自不同任務(wù)源的task會(huì)被放入不同的task對(duì)列:比如,用戶代理會(huì)為鍵盤事件分配一個(gè)task對(duì)列,為其它事件分配另外的對(duì)列。

task的執(zhí)行是根據(jù)進(jìn)入事件決定的,先進(jìn)隊(duì)列先執(zhí)行

task來源主要有以下幾種

  • script代碼
  • setTimeout/setInterval
  • I/O
  • UI交互
  • setImmediate(nodejs環(huán)境中)

microtask

一個(gè)事件循環(huán)只有一個(gè)microtask隊(duì)列,通常有以下幾種

  • promise(promise中then和catch才是microtask,本身內(nèi)部代碼不是)
  • MutationObserver
  • promise.nextTick(Nodejs中)

Event Loop工作過程

一個(gè)Event Loop主要存在就會(huì)不斷執(zhí)行下面過程

  • 1.在所有的task隊(duì)列中選擇一個(gè)最早進(jìn)入的隊(duì)列的task,用戶代理可以選擇任何的task,如果沒選擇的話,就跳到6Microtask
  • 2.將前一步選擇的task設(shè)置為currently running task
  • run:運(yùn)行被選擇的task
  • 運(yùn)行結(jié)束后,將Event Loop中的currently running task設(shè)置為null
  • 從task隊(duì)列里移除前邊run里運(yùn)行的task
  • Microtask:執(zhí)行microtask中的所有任務(wù)
  • 更新渲染
  • 如果這是一個(gè)work event loop,但是task隊(duì)列中沒有任務(wù),并且WorkerGlobalScope對(duì)象的closing標(biāo)識(shí)符為true,則銷毀Event Loop,中止這些步驟,然后run a work
  • 返回第一步

其實(shí)就是說,Event Loop執(zhí)行的時(shí)候就是每次執(zhí)行一個(gè)task任務(wù)(宏任務(wù)),然后運(yùn)行所有的微任務(wù),以此循環(huán)下去

下面示例代碼

setTimeout(() => console.log('setTimeout1'), 0);
setTimeout(() => {
    console.log('setTimeout2');
    Promise.resolve().then(() => {
        console.log('promise2');
        Promise.resolve().then(() => {
            console.log('promise3');
        })
        console.log(5)
    })
    setTimeout(() => console.log('setTimeout4'), 0);
}, 0);
setTimeout(() => console.log('setTimeout3'), 0);
Promise.resolve().then(() => {
    console.log('promise1');
})

結(jié)果如下

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

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

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