js基礎 ---- 事件迴圈機制
一、為什麼要事件迴圈機制
我們都知道 JavaScript 是一門單執行緒非阻塞的指令碼語言,這就意味著再任何時候 JavaScript 都只會有一個主執行緒來處理所有的任務
但是對於 JavaScript 來說單執行緒確實是必要的 原因之一在其最初也是最主要的執行環境——瀏覽器中,我們需要進行各種各樣的dom操作。這樣就保證了程式執行的一致性。
但是隨著 JavaScript 的發展 現在單執行緒的效率實在太低 這就衍生出了非同步的概念 非同步的關鍵就是 event loop(事件迴圈)
二、什麼是事件迴圈機制
js引擎遇到一個非同步事件後並不會一直等待其返回結果,而是會將這個事件掛起,繼續執行執行棧中的其他任務。當一個非同步事件返回結果後,js會將這個事件加入與當前執行棧不同的另一個佇列,我們稱之 為事件佇列
三、什麼是執行棧和事件佇列
當 JavaScript程式碼執行的時候會將不同的變數存於記憶體中的不同位置:堆(heap)和棧(stack)中來加以區分
堆裡存放著一些物件,棧中則存放著一些基礎型別變數以及物件的指標
(1)、執行棧
當我們呼叫一個方法的時候,js會生成一個與這個方法對應的執行環境(context),又叫執行上下文。這個執行環境中存在著這個方法的私有作用域,上層作用域的指向,方法的引數,這個作用域 中定義的變數以及這個作用域的this物件。 而當一系列方法被依次呼叫的時候,因為js是單執行緒的,同一時間只能執行一個方法,於是這些方法被排隊在一個單獨的地方。這個地方被稱為執行棧。
執行棧的工作程序:
當一個指令碼第一次執行的時候,js引擎會解析這段程式碼,並將其中的同步程式碼按照執行順序加入執行棧中,然後從頭開始執行。如果當前執行的是一個方法,那麼js會向執行棧中新增這個方法的執 行環境,然後進入這個執行環境繼續執行其中的程式碼。當這個執行環境中的程式碼 執行完畢並返回結果後,js會退出這個執行環境並把這個執行環境銷燬,回到上一個方法的執行環境。。這個過程反進 行,直到執行棧中的程式碼全部執行完畢。
(2)、事件佇列
當我們發出一個ajax請求,他並不會立刻返回結果,為了防止瀏覽器出現假死或者空白,主執行緒會把這個非同步任務掛起(pending),繼續執行執行棧中的其他任務,等非同步任務返回結果後,js會將這 個非同步任務按照執行順序,加入到與執行棧不同的另一個佇列,也就是事件佇列。
四、非同步任務的執行順序
因為非同步任務之間並不相同,因此他們的執行優先順序也有區別。不同的非同步任務被分為兩類:微任務(micro task)和巨集任務(macro task)
巨集任務:
setInterval()
setTimeout()
微任務:
new Promise()
new MutaionObserver()
在一個事件迴圈中,非同步事件返回結果後會被放到一個任務佇列中。然而,根據這個非同步事件的型別,這個事件實際上會被對應的巨集任務佇列或者微任務佇列中去
並且在當前執行棧為空的時候,主執行緒會 檢視微任務佇列是否有事件存在,
如果不存在,那麼再去巨集任務佇列中取出一個事件並把對應的回到加入當前執行棧
如果存在,則會依次執行佇列中事件對應的回撥,直到微任務佇列為空,然後去巨集任務佇列中取出最前面的一個事件,把對應的回撥加入當前執行棧...如此反覆,進入迴圈。
注意點:
噹噹前執行棧執行完畢時會立刻先處理所有微任務佇列中的事件,然後再去巨集任務佇列中取出一個事件。同一次事件迴圈中,微任務永遠在巨集任務之前執行