EventLoop

三個關(guān)鍵字:

EventLoop: 事件循環(huán)
MicroTask: 微任務(wù)
MacroTask: 宏任務(wù)

三個要點:

  1. js是單線程語言
  2. 任務(wù)隊列
  3. EventLoop 是js的執(zhí)行機制

一 js為什么是單線程語言?

js是單線程語言與它的用途相關(guān),js是瀏覽器腳本語言,主要用于與用戶的交互以及操作dom節(jié)點,所以要避免復(fù)雜的同步問題,這就決定了它必須是單線程的。比如:假如js是多線程的,一個線程在dom節(jié)點上添加內(nèi)容,一個線程刪除該dom節(jié)點,這時瀏覽器該怎么執(zhí)行?

二 任務(wù)隊列

2.1 同步任務(wù)和異步任務(wù)
由于js是單線程的,所以就意味著任務(wù)需要排隊執(zhí)行,任務(wù)又分為同步任務(wù)和異步任務(wù)。

同步任務(wù):在主線程上排隊執(zhí)行的任務(wù),只有前一個任務(wù)執(zhí)行完才會執(zhí)行下一個任務(wù),如果前一個任務(wù)耗時長,后一個任務(wù)就會一直等待,容易造成阻塞。
異步任務(wù):最開始不進入主線程,而是進入任務(wù)隊列中的任務(wù),當(dāng)主線程執(zhí)行完畢,系統(tǒng)會去查看任務(wù)隊列,然后決定執(zhí)行哪個異步任務(wù),異步任務(wù)的執(zhí)行順序就和EventLoop機制,即微任務(wù)和宏任務(wù)相關(guān)。

異步執(zhí)行的運行機制:

  1. 首先執(zhí)行主線程上的代碼,形成執(zhí)行棧,遇到異步任務(wù)就將它放入任務(wù)隊列中。
  2. 主線程執(zhí)行完畢,系統(tǒng)會查看任務(wù)隊列,某個任務(wù)可以執(zhí)行時,就進入執(zhí)行棧中開始執(zhí)行。(如果有微任務(wù)就執(zhí)行所有的微任務(wù),沒有微任務(wù)就執(zhí)行下一個宏任務(wù), 每個宏任務(wù)執(zhí)行完都要清空所有的微任務(wù))。

2.2 異步任務(wù)之微任務(wù)與宏任務(wù)
js是單線程的,所有的異步任務(wù)都被放入到任務(wù)隊列當(dāng)中,任務(wù)隊列又分為兩類:微任務(wù)和宏任務(wù)。
微任務(wù):

  process.nextTick(先于promise執(zhí)行)
  promise
  Object.observe
  MutaionObserver

宏任務(wù):

  setTimeout
  setInterval
  setImmediate
  IO
  UI rerendering

三 Event Loop

主線程從任務(wù)隊列中讀取任務(wù)的過程是循環(huán)的,所以整個的運行機制被稱為 Event Loop(事件循環(huán))。
例子:

console.log('main1');
process.nextTick(function() {
    console.log('process.nextTick1');
});
setTimeout(function() {
    console.log('setTimeout');
    process.nextTick(function() {
        console.log('process.nextTick2');
    });
}, 0);
new Promise(function(resolve, reject) {
    console.log('promise');
    resolve();
}).then(function() {
    console.log('promise then');
});
console.log('main2');

結(jié)果:

  1. 開始執(zhí)行代碼輸出main1, process.nextTick放到執(zhí)行棧末尾,setTimeout 放到宏任務(wù),new promise 立即執(zhí)行輸出promise,then方法放到微任務(wù),執(zhí)行最后一句代碼輸出main2。
  2. 主線程執(zhí)行完畢,開始清空微任務(wù),執(zhí)行process.nextTick輸出process.nextTick1, 在執(zhí)行then輸出promise then。
  3. 執(zhí)行下次event loop,執(zhí)行setTimeout輸出 setTimeout,里面的 process.nextTick放到當(dāng)前執(zhí)行棧末尾,當(dāng)前宏任務(wù)執(zhí)行完畢,執(zhí)行微任務(wù),輸出process.nextTick2。

四 node中的Event Loop

Node的執(zhí)行過程
  1. v8引擎解析js腳本
  2. 調(diào)用node API
  3. libuv庫執(zhí)行node api, 將不同的任務(wù)分給不同的線程執(zhí)行,以異步的形式將結(jié)果返回給v8引擎
  4. v8引擎將結(jié)果返回給用戶
Node的EventLoop的過程

Node的EventLoop由6個任務(wù)階段組成,每個階段都有自己的任務(wù)隊列,每當(dāng)進入某個階段,都會從所屬的隊列中取出任務(wù)來執(zhí)行,當(dāng)隊列為空或者被執(zhí)行任務(wù)的數(shù)量達到系統(tǒng)的最大數(shù)量時,進入下一階段。這六個階段都執(zhí)行完畢稱為一輪循環(huán)。
六個階段具體而言:

  1. timers:執(zhí)行setTimeout() 和 setInterval()中到期的callback。
  2. I/O callbacks:上一輪循環(huán)中有少數(shù)的I/Ocallback會被延遲到這一輪的這一階段執(zhí)行
  3. idle, prepare:僅內(nèi)部使用,process.nextTick就屬于這一類
  4. poll:最為重要的階段,執(zhí)行I/O callback,在適當(dāng)?shù)臈l件下會阻塞在這個階段
  5. check:執(zhí)行setImmediate()的callback
  6. close callbacks:執(zhí)行close事件的callback,例如socket.on("close",func)


    Node的EventLoop.png

參考文章:
https://www.cnblogs.com/wuguanglin/p/EventLoop.html(Node的EventLoop機制講的比較好)

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

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

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