Javascript執行機制之Event Loop
目錄
- 一、四個概念
- 1、是單執行緒
- 2、任務佇列
- 3、同步任務、非同步任務
- 4、script執行棧
- 二、Event Loop
- 三、實戰
一、四個概念
1、Javascript是單執行緒
單執行緒意味著我們的程式碼只能從上往下同步執行,同一時間只能執行一個任務,這會導致某些執行時間較長或者執行時間不確定的任務會卡住其它任務的正常執行,
Event Loop
出現的原因正是為了解決此問題。
2、任務佇列
為了解決上述的排隊問題,有了任務佇列,瀏覽器在非同步任務有了結果後,將其新增到任務佇列,以待將來執行,其他任務就在主執行緒上同步執行。
這裡要注意的是,向任務佇列中新增任務的時機是非同步任務有結果後。其實任務佇列中存在的就是非同步任務的回撥函式。
3、同步任務、非同步任務
Js程式中的同步任務是指在主執行緒中執行的任務,非同步任務是指進入任務佇列中的任務
4、Javascript執行棧
所有的同步任務都在主執行緒上執行,行成一個執行棧。當主執行緒上任務執行完畢後,從任務佇列中取出任務執行。
var name = "zhouwei"; setTimeout(() => { console.log(1); },1000); console.log(name);
上面程式碼在瀏覽器中執行如下,我們將程式全域性執行環境的程式碼理解為包裹在一個main函式中的程式碼,這段程式碼的執行棧變化如下圖:
- 開始
main
任務(全域性程式碼入棧執行),當遇到非同步任務(setTimeout
後)。 - 瀏覽器接管非同步任務,並在1s後將非同步任務的結果(回撥函式)新增到任務佇列。
- 執行棧中的同步任務執行完畢,此時任務佇列為空(未到1s),執行棧也為空
- 非同步任務有結果後,首先進入任務佇列排隊(因為可能有很多非同步任務)。http://www.cppcns.com
- 執行棧從任務佇列中取出任務開始同步執行。
- 重複執行第5步。
二、Event Loop
Js執行棧不斷的從任務佇列中讀取任務並執行的過程就是Event Loop
我們知道任務佇列中存放的是非同步任務的結果,那麼非同步任務都有哪些了?
- 1、事件
Javascript
中的事件有很多,都是屬於非同步任務。由瀏覽器接管,當事件觸發時,將事件的回撥加入的任務佇列中,在Js執行棧中沒有任務時執行。
- 2、Http請求
- 3、定時器
- 4、requestAnimationFrame等
巨集任務(macrotask
)和微任務(microtask
)
在瞭解了任務佇列和Event Loop
後,我們知道了Js執行棧從任務佇列中讀取任務執行,但這個具體工程我們任務不清楚,這裡引出了巨集任務和微任務的的概念,幫助我們理解Event Loop。
進入任務佇列中的非同步任務回撥分為了巨集任務和微任務, Js執行棧執行巨集任務和微任務的規則如下圖所示。
Js執行棧首先執行一個巨集任務(全域性程式碼) -> 從任務佇列中讀取所有微任務執行 -> UI rendering(瀏覽器渲染介面) -> 從任務佇列讀取一個巨集任務 -> 所有微任務 -> UI rendering -> …
在每一輪的Event Loop結束後(1個巨集任務 + 所有微任務),瀏覽器開始渲染介面(如果有需要渲染的UI,否則不執行UI rendering),在
UI rendering
結束後,開始下一輪Event Loop。
哪些是巨集任務?
- setTimeout
- setInterval
- setImmediate (Node)
- requestAnimationFrame (瀏覽器)
- I/O (事件回撥)
- UI rendering (瀏覽器渲染)
哪些是微任務?
- Promise
- process.nextTick (Node)
- MutationObserver (現代瀏覽器提供的用來檢測 DOM 變化的介面)
setTimeout延時問題
一般來說在程式碼中setTimeout客棧
中回撥的執行時間都是大於設定的時間。 這是因為在setTimeout
指定時間到達後,雖然回撥函式被新增到了任務佇列,但是此時Js執行棧中可能有正在執行的任務,此回撥需要等待Js執行棧的任務執行完畢後才有機會執行,這就是setTimeout
延時問題。
三、實戰
練習下下方程式碼輸出結果吧:
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執行機制來分析這道題:
1: 執行全域性任務中的同步程式碼輸出:
1
4
www.cppcns.com8
這裡需要注意的是Promise
接受的handle
函式是同步任務,而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); })
執行一個微任務, 輸出:6
3: 接著執行第一個巨集任務
setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3) }); });
輸出:2
在此巨集任務中,向任務 佇列添加了一個微任務。此時任務佇列有了新的微任務。
4:執行一個微任務,輸出:3
then(() => { console.log(3) });
5: 繼續按照規則執行任務, 輸出: 5、7
整體輸出情況是:
1、4、8、6、2、3、5、7
你的答案是不是這樣呢?
總結:
javascritp
的任務分為同步任務和非同步任務- 同步任務在主執行緒(Js執行棧)中執行,非同步任務被其他執行緒接管,並在非同步任務有結果www.cppcns.com後,將其回撥新增到任務佇列。
- 任務佇列中的任務分為了巨集任務和微任務。Js執行棧總是先執行一個巨集任務,再執行完所有微任務…
到此這篇關於Javascript
執行機制之Event Loop的文章就介紹到這了,更多相關Javascript執行機制Event Loop內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!