Javascript運行機制之Event Loop
一、四個概念
1、Javascript是單線程
單線程意味著我們的js代碼只能從上往下同步執(zhí)行,同一時間只能執(zhí)行一個任務,這會導致某些執(zhí)行時間較長或者執(zhí)行時間不確定的任務會卡住其它任務的正常執(zhí)行,Event Loop出現(xiàn)的原因正是為了解決此問題。
2、任務隊列
為了解決上述的排隊問題,有了任務隊列,瀏覽器在異步任務有了結果后,將其添加到任務隊列,以待將來執(zhí)行,其他任務就在主線程上同步執(zhí)行。
這里要注意的是,向任務隊列中添加任務的時機是異步任務有結果后。其實任務隊列中存在的就是異步任務的回調函數(shù)。
3、同步任務、異步任務
Js程序中的同步任務是指在主線程中執(zhí)行的任務,異步任務是指進入任務隊列中的任務
4、Javascript執(zhí)行棧
所有的同步任務都在主線程上執(zhí)行,行成一個執(zhí)行棧。當主線程上任務執(zhí)行完畢后,從任務隊列中取出任務執(zhí)行。
var name = "zhouwei";
setTimeout(() => {
console.log(1);
}, 1000);
console.log(name);
上面代碼在瀏覽器中執(zhí)行如下,我們將程序全局執(zhí)行環(huán)境的代碼理解為包裹在一個main函數(shù)中的代碼,這段代碼的執(zhí)行棧變化如下圖:

- 開始執(zhí)行代碼,將
main任務(全局代碼入棧執(zhí)行),當遇到異步任務(setTimeout后)。 - 瀏覽器接管異步任務,并在1s后將異步任務的結果(回調函數(shù))添加到任務隊列。
- 執(zhí)行棧中的同步任務執(zhí)行完畢,此時任務隊列為空(未到1s),執(zhí)行棧也為空
- 異步任務有結果后,首先進入任務隊列排隊(因為可能有很多異步任務)。
- 執(zhí)行棧從任務隊列中取出任務開始同步執(zhí)行。
- 重復執(zhí)行第5步。
二、Event Loop
Js執(zhí)行棧不斷的從任務隊列中讀取任務并執(zhí)行的過程就是Event Loop
我們知道任務隊列中存放的是異步任務的結果,那么異步任務都有哪些了?
- 1、事件
Javascript中的事件有很多,都是屬于異步任務。由瀏覽器接管,當事件觸發(fā)時,將事件的回調加入的任務隊列中,在Js執(zhí)行棧中沒有任務時執(zhí)行。
- 2、Http請求
- 3、定時器
- 4、requestAnimationFrame等
宏任務(macrotask)和微任務(microtask)
在了解了任務隊列和Event Loop后,我們知道了Js執(zhí)行棧從任務隊列中讀取任務執(zhí)行,但這個具體工程我們任務不清楚,這里引出了宏任務和微任務的的概念,幫助我們理解Event Loop。
進入任務隊列中的異步任務回調分為了宏任務和微任務, Js執(zhí)行棧執(zhí)行宏任務和微任務的規(guī)則如下圖所示。

Js執(zhí)行棧首先執(zhí)行一個宏任務(全局代碼) -> 從任務隊列中讀取所有微任務執(zhí)行 -> UI rendering(瀏覽器渲染界面) -> 從任務隊列讀取一個宏任務 -> 所有微任務 -> UI rendering -> …
在每一輪的Event Loop結束后(1個宏任務 + 所有微任務),瀏覽器開始渲染界面(如果有需要渲染的UI,否則不執(zhí)行UI rendering),在
UI rendering結束后,開始下一輪Event Loop。
哪些是宏任務?
- setTimeout
- setInterval
- setImmediate (Node)
- requestAnimationFrame (瀏覽器)
- I/O (事件回調)
- UI rendering (瀏覽器渲染)
哪些是微任務?
- Promise
- process.nextTick (Node)
- MutationObserver (現(xiàn)代瀏覽器提供的用來檢測 DOM 變化的網頁接口)
setTimeout延時問題
一般來說在代碼中setTimeout中回調的執(zhí)行時間都是大于設置的時間。 這是因為在setTimeout指定時間到達后,雖然回調函數(shù)被添加到了任務隊列,但是此時Js執(zhí)行棧中可能有正在執(zhí)行的任務,此回調需要等待Js執(zhí)行棧的任務執(zhí)行完畢后才有機會執(zhí)行,這就是setTimeout延時問題。
三、實戰(zhàn)
練習下下方代碼輸出結果吧:
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3)
});
});
new Promise(resolve => {
console.log(4);
setTimeout(() => {
console.log(5);
});
resolve(6)
}).then(data => {
console.log(data);
})
setTimeout(() => {
console.log(7);
})
console.log(8);
用上方的我們說過的js執(zhí)行機制來分析這道題:
1: 執(zhí)行全局任務中的同步代碼輸出:
1
4
8
這里需要注意的是Promise接受的handle函數(shù)是同步任務,而then方法是異步任務,所以會直接輸出4。
2: 這時的任務隊列中有三個setTimeout的宏任務,和一個Promise的微任務
// 此時的宏任務是
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3)
});
});
setTimeout(() => {
console.log(5);
});
setTimeout(() => {
console.log(7);
})
// 此時微任務是
then(data => {
console.log(data);
})
執(zhí)行一個微任務, 輸出:6
3: 接著執(zhí)行第一個宏任務
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3)
});
});
輸出:2
在此宏任務中,向任務 隊列添加了一個微任務。此時任務隊列有了新的微任務。
4:執(zhí)行一個微任務,輸出:3
then(() => {
console.log(3)
});
5: 繼續(xù)按照規(guī)則執(zhí)行任務, 輸出: 5、7
整體輸出情況是:
1、4、8、6、2、3、5、7
你的答案是不是這樣呢?
總結:
javascritp的任務分為同步任務和異步任務- 同步任務在主線程(Js執(zhí)行棧)中執(zhí)行,異步任務被其他線程接管,并在異步任務有結果后,將其回調添加到任務隊列。
- 任務隊列中的任務分為了宏任務和微任務。Js執(zhí)行??偸窍葓?zhí)行一個宏任務,再執(zhí)行完所有微任務…
到此這篇關于Javascript運行機制之Event Loop的文章就介紹到這了,更多相關Javascript運行機制Event Loop內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
js實現(xiàn)QQ面板拖拽效果(慕課網DOM事件探秘)(全)
這篇文章主要為大家詳細介紹了QQ面板拖拽效果,探秘慕課網DOM事件,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09
JavaScript變量聲明var,let.const及區(qū)別淺析
這篇文章主要介紹了JavaScript變量聲明var,let.const及區(qū)別淺析,需要的朋友可以參考下2018-04-04

