1. 程式人生 > 實用技巧 >關於事件迴圈:Event Loop事件的一些總結

關於事件迴圈:Event Loop事件的一些總結

什麼是事件迴圈?

JavaScript是一門單執行緒語言,即當有一個任務在執行的時候,其他任務需要在其後方等待。而在實際場景中,有些任務可以放在比較靠後的位置,比如載入網路資源時候傳送的非同步請求。總而言之,事件迴圈就是JavaScript非同步執行機制的一種實現方式。

我們經常性地認知是,JavaScript是按照順序從上往下執行的,但是看如下程式碼:

setTimeout(()=>{
console.log(111)},0)

cosole.log(222)

實際上真實操作的話是輸出順序為222,111。這是為什麼呢?

Javascript引擎會智慧地將任務分佈為同步任務與非同步任務。同步任務地話:主程式碼。非同步任務的話,如:setTimeout、Promise、setInterval、nextTick等。

首先Javascript引擎會將整體script程式碼放入執行棧中,遇到同步任務則直接進入主執行緒執行,遇到非同步任務時註冊回撥函式,並將非同步任務放入事件佇列中等待。

待主執行緒中的所有同步任務執行完畢後,才會去事件佇列處取任務。根據佇列先進先出的特點,最先進入佇列的非同步任務會被率先執行。

而在實際場景中,並不是從事件佇列中挨個取出非同步任務直接執行那麼簡單。從巨集觀意義上,我們將JavaScript的任務分為同步任務與非同步任務,但在事件迴圈中,我們將其分為了巨集任務與微任務。不同型別的任務會進入不同的事件佇列中。

巨集任務:一般的JavaScript同步程式碼,定時器相關的非同步程式碼setTimeout、setInterval。

微任務:promise、nextTick等。

在Javascript引擎執行程式碼的過程當中,首先將整體程式碼作為一個巨集任務執行,遇到一般的同步程式碼立即執行,遇到定時器相關如setTimeout這種巨集任務,則先將其放入到巨集任務事件佇列中,遇到Promise這種則將其catch、then函式放到微任務佇列中。整體程式碼,作為第一次巨集任務執行完畢後,JavaScript引擎會先到微任務佇列中看看有沒有任務,如果有的話,就會將其全部執行完畢;如果沒有的話,則會進入到下一次巨集任務中。也就是說,JavaScript執行機制是按照巨集任務-微任務-巨集任務-微任務..這樣子迴圈執行。

根據佇列先進先出的特點,取出巨集任務事件佇列中第一個巨集任務會先執行,遇到同步程式碼立即執行,遇到微任務則繼續進入微任務事件佇列。待這一次的巨集任務執行完畢後,則繼續執行所有微任務。如此迴圈下去,即為事件迴圈。

setTimeout(()=>{
console.log(111)},1000);

new Promise((resolve,reject) =>{
console.log(222);
resolve();}).then(() =>{
console.log(333);});
console.log(444)

我們可以把上面的程式碼看成兩次事件迴圈。

我們將整體程式碼作為巨集任務讓其進入主執行緒中執行,由於setTimeout事件為一個巨集任務,因此我們把它放在巨集任務事件隊列當中。往下執行的時候,遇到promise裡面的立即執行語句,因此我們直接輸出222;遇到then函式,我們將其放入到微任務事件隊列當中,放在較為後面的順序。後期還有個444,直接執行輸出。接著我們按照巨集任務-微任務-巨集任務的順序,可知下一步執行的即為微任務,promise中的then函式,則輸出333,最後再輸出setTimeout中的111。

因此順序為:222、444、333、111.

注意:即使setTimeout函式的延遲時間設定為0,也不會立即執行。這邊將setTimeout的延遲時間設定為0的意思是待主執行緒空閒後便可執行。因此還是得等到主執行緒按照巨集-微-巨集這樣的事件迴圈順序執行下去。