【執行機制】 JavaScript的事件迴圈機制總結 eventLoop
阿新 • • 發佈:2020-12-10
### 0、從個例子開始
```
//code-01
console.log(1)
setTimeout(() => {
console.log(2);
});
console.log(3);
```
稍微有點前端經驗的人都知道這段程式碼輸出的應該是 `1 3 2`,因為setTimeout函式是非同步執行。
那麼都說js語言是單執行緒的,就是說是一件事從頭到尾做完,那麼它是怎麼做到非同步的呢?
這就要說到瀏覽器的執行機制了。
### 1、瀏覽器的基本機制
關於這部分內容目前瞭解不是很多,以後有時間再補上詳細的內容。瀏覽器程序大致分為如下幾個部分(從其他地方拷的圖):
![](https://img2020.cnblogs.com/blog/2210844/202012/2210844-20201210124100144-1435301681.png)
我們所說的js單執行緒,是指瀏覽器的`js引擎執行緒`只有一個,用來執行js的程式碼,而瀏覽器的`定時觸發器執行緒`和`事件觸發器執行緒`結合,可以實現js語言的非同步邏輯。那麼js到底是怎麼執行非同步操作的呢?
我們來看下面這張流程圖:
### 2、js語言事件迴圈機制-基礎
![](https://img2020.cnblogs.com/blog/2210844/202012/2210844-20201210152318954-469673948.png)
我們來根據以上流程圖,再來看 上面程式碼 code-01,
>1.程式碼開始執行,遇到console.log(1) ,列印 1
>2.程式碼繼續執行,遇到setTimeout,此為非同步任務,交給`非同步處理模組`(這裡可能是`定時觸發器執行緒`),因為沒有延遲時間,所以`console.log(2)`很快加入到了`事件佇列`中,因為同步任務沒有執行完,所以現在不能執行
>3.程式碼繼續執行,遇到console.log(3),列印 3
>4.程式碼同步任務執行完畢,檢視`事件佇列`中是否有任務,發現有`console.log(2)`,於是列印 2
### 3、js語言事件迴圈機制-巨集任務與微任務
經過上面的分析,我們對 事件迴圈機制有了初步的瞭解,現在我們再來看一個例子:
```
// code-02
console.log(1)
setTimeout(() => {
console.log(2);
});
new Promise(function(resolve){
console.log(3)
resolve()
}).then(function(){
console.log(4)
})
console.log(5);
```
上面程式碼的結果為`1 3 5 4 2`,
我們知道 promise.then和setTimeout都是非同步事件,那為什麼then會比setTimeout先執行呢?
其實是因為上面流程圖中 `事件佇列` 其實應該分為 `巨集任務佇列`和`微任務佇列`,`微任務`優先於`巨集任務`,而且要等`微任務佇列`清空,才會去取`巨集任務佇列`中的任務。
**所以以上流程圖應改為:**
![](https://img2020.cnblogs.com/blog/2210844/202012/2210844-20201210163507026-241624473.png)
**我們再來根據以上更新的流程圖,再來看 上面程式碼 code-02,**
>1.程式碼開始執行,遇到console.log(1) ,列印 1
>2.程式碼繼續執行,遇到setTimeout,此為非同步任務,交給`非同步處理模組`,因為沒有延遲時間,所以`console.log(2)`很快加入到了`巨集任務佇列`中
>3.程式碼繼續執行,遇到console.log(3),列印 3
>4.程式碼繼續執行,遇到then函式,此為非同步任務,交給`非同步處理模組`,因為promise馬上就resolve,所以`console.log(4)`很快加入到了`微任務佇列`中
>5.程式碼繼續執行,遇到遇到console.log(5) ,列印 5
>6.程式碼同步任務執行完畢,檢視`微任務佇列`中是否有任務,發現有`console.log(4)`,於是 列印 4
>7.`微任務佇列`被清空,檢視`巨集任務佇列`中是否有任務,發現有`console.log(2)`,於是 列印 2
**那麼到底有哪些非同步任務是`巨集任務`,哪些是`微任務`呢?**
> 常見的巨集任務
>> 1.script程式碼(整體的外層程式碼其實就是第一個巨集任務)
>> 2.setTimeout,setInterval,setImmediate
>> 3. i/o事件
>> 4. UI事件,比如點選事件
> 常見的微任務
>> promise
>> process.nextTick(Node.js)
### 4、最後一個例子 - 最少延遲時間
我們再來看最後一個例子
```
setTimeout(() => {
console.log(1);
},2);
setTimeout(() => {
console.log(2);
},1);
setTimeout(() => {
console.log(3);
},0);
```
執行結果為 `2 3 1`
可能會有人疑惑,照以上的邏輯,不應該是 `3 2 1`嗎? 這是因為 setTimeout官方給出的規定是:最低延遲為 4ms,(這個有限制條件,但沒怎麼看懂)
但這個最低時間不同環境好像實現的不太一樣
就上面程式碼而言,在Chorme瀏覽器中,最低延遲1ms,就是說 0ms 和 1ms 是同樣的,
所以根據程式碼順序,`console.log(2)`比`console.log(3)`先進入 `巨集任務佇列`
### 5、總結
>1. js是單執行緒,只能順序執行程式碼, 但是瀏覽器有其他執行緒可以處理非同步情況
> 2. js引擎執行程式碼時,遇到同步任務則順序執行,遇到非同步任務則交由 `非同步事件處理模組`處理
> 3. `非同步事件處理模組`等事件觸發條件達成後,將非同步任務分別 加入`巨集任務佇列`和`微任務佇列`
> 4. 同步任務執行完畢後,先執行`微任務佇列`任務,等佇列清空時,執行`巨集任務佇列`
> 5. 每一個`巨集任務` 重複 2 步驟
**參考**
1.[Event Loop的規範和實現](https://zhuanlan.zhihu.com/p/33087629)
2.[這一次,徹底弄懂 JavaScript 執行機制](https://juejin.cn/post/6844903512845860872)
3.[setTimeout和setImmediate到底誰先執行,本文讓你徹底理解Event Loop](https://www.cnblogs.com/dennisj/p/1255099