1. 程式人生 > >【執行機制】 JavaScript的事件迴圈機制總結 eventLoop

【執行機制】 JavaScript的事件迴圈機制總結 eventLoop

### 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