爲何會寫這篇博文呢?javascript
前段時間,和頭條的小夥伴聊天問頭條面試前端會問哪些問題,他稱若是是他面試的話,event-loop確定是要問的。那天聊了蠻多,event-loop算是給我留下了很深的印象。緣由很簡單,由於以前我從未深刻了解過,若是是面試的時候,我遇到了這個問題,估計回答得確定不如人意。html
所以,最近我閱讀了一些相關的文章,並細細梳理了一番,輸出了本篇博文,但願能幫助你們搞懂瀏覽器的event-loop。後續會繼續補充node中的event-loop。前端
更多文章可戳: github.com/YvetteLau/B…java
JavaScript的運行機制:node
(1)全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。git
(2)主線程以外,還存在"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。github
(3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。面試
(4)主線程不斷重複上面的第三步shell
歸納便是: 調用棧中的同步任務都執行完畢,棧內被清空了,就表明主線程空閒了,這個時候就會去任務隊列中按照順序讀取一個任務放入到棧中執行。每次棧內被清空,都會去讀取任務隊列有沒有任務,有就讀取執行,一直循環讀取-執行的操做segmentfault
一個事件循環中有一個或者是多個任務隊列
JavaScript中有兩種異步任務:
宏任務: script(總體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering
微任務: process.nextTick(Nodejs), Promises, Object.observe, MutationObserver;
主線程從"任務隊列"中讀取執行事件,這個過程是循環不斷的,這個機制被稱爲事件循環。此機制具體以下:主線程會不斷從任務隊列中按順序取任務執行,每執行完一個任務都會檢查microtask隊列是否爲空(執行完一個任務的具體標誌是函數執行棧爲空),若是不爲空則會一次性執行完全部microtask。而後再進入下一個循環去任務隊列中取下一個任務執行。
詳細說明:
執行進入microtask檢查的的具體步驟以下:
須要注意的是:當前執行棧執行完畢時會馬上先處理全部微任務隊列中的事件, 而後再去宏任務隊列中取出一個事件。同一次事件循環中, 微任務永遠在宏任務以前執行。
圖示:
先看一個簡單的示例:
setTimeout(()=>{
console.log("setTimeout1");
Promise.resolve().then(data => {
console.log(222);
});
});
setTimeout(()=>{
console.log("setTimeout2");
});
Promise.resolve().then(data=>{
console.log(111);
});
複製代碼
思考一下, 運行結果是什麼?
運行結果爲:
111
setTimeout1
222
setTimeout2
複製代碼
咱們來看一下爲何?
咱們來詳細說明一下, JS引擎是如何執行這段代碼的:
再思考一下下面代碼的執行順序:
console.log('script start');
setTimeout(function () {
console.log('setTimeout---0');
}, 0);
setTimeout(function () {
console.log('setTimeout---200');
setTimeout(function () {
console.log('inner-setTimeout---0');
});
Promise.resolve().then(function () {
console.log('promise5');
});
}, 200);
Promise.resolve().then(function () {
console.log('promise1');
}).then(function () {
console.log('promise2');
});
Promise.resolve().then(function () {
console.log('promise3');
});
console.log('script end');
複製代碼
思考一下, 運行結果是什麼?
運行結果爲:
script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0
複製代碼
那麼爲何?
咱們來詳細說明一下, JS引擎是如何執行這段代碼的:
由於 JavaScript 是單線程的。單線程就意味着,全部任務須要排隊,前一個任務結束,纔會執行後一個任務。若是前一個任務耗時很長,後一個任務就不得不一直等着。爲了協調事件(event),用戶交互(user interaction),腳本(script),渲染(rendering),網絡(networking)等,用戶代理(user agent)必須使用事件循環(event loops)。
最後有一點須要注意的是:本文介紹的是瀏覽器的Event-loop,所以在測試驗證時,必定要使用瀏覽器環境進行測試驗證,若是使用了node環境,那麼結果不必定是如上所說。
最後,若是您以爲本篇博文給了您一點幫助或者啓發,請幫忙點個Star吧~ github.com/YvetteLau/B…
推薦關注本人公衆號