Js - 運行機制 (Even Loop)node
JavaScript的一個語言特性(也是這門語言的核心)就是單線程。什麼是單線程呢?簡單地說就是同一時間只能作一件事,當有多個任務時,只能按照一個順序一個完成了再執行下一個。promise
那爲何JS是單線程的呢?瀏覽器
爲了提升CPU的利用率,HTML5提出Web Worker標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。因此這個標準並無改變JavaScript單線程的本質;多線程
任務隊列 Task queue異步
在Javascript中,全部的任務分爲兩類:同步任務和異步任務
函數
同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;oop
異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。post
簡要說明:在這裏說到了 ‘主線程’ 和 ‘任務隊列’ ,我的簡單理解: 主線程就是 Js 執行的線程 , 任務隊列是異步任務暫時存放的一個事件隊列;spa
在Js執行中,同步任務和異步任務分別進入不一樣的執行"場所",同步的進入主線程,異步的進入 Event Table 並註冊函數。 線程
當指定的事情完成時,Event Table 會將這個函數移入 Event Queue (事件隊列)。
主線程內的任務執行完畢爲空,會去 Event Queue 讀取對應的函數,進入主線程執行。
上述過程會不斷重複,也就是咱們常說的 Event Loop(事件循環)。
經過上邊的描述,咱們來看一張圖更加清晰的瞭解 Event Loop (事件循環) 機制
接下來咱們看一個例子:
setTimeout(function(){ console.log('1') }); new Promise(function(resolve){ console.log('2'); resolve(); }).then(function(){ console.log('3') }); console.log('4');
首先setTimeout 是異步進入 事件隊列,而後 promise 的 then 也是異步 進入事件隊列 ,
那麼按照咱們上邊說的Js執行機制,先走主線程的同步任務,打印2 ,而後4,緊跟着執行異步任務,也就是任務隊列打印1,隨後是 3 , 因此結果應該是 2,4,1,3, 事實真的是這樣子嘛 ? 接着往下看 :
Js 中的宏任務和微任務 - 略記一下
macro-task(宏任務) :包括總體代碼 script,setTimeout,setInterval
micro-task(微任務) : Promise,process.nextTick
(process.nextTick(callback)
相似node.js版的"setTimeout",在事件循環的下一次循環中調用 callback 回調函數)
咱們上邊的 setTimeout 放到了 event queue 事件隊列裏 , promise 的 then 函數 也被放到了 event queue 事件隊列裏,然而杯具來了,這兩個 queue 並非一個隊列;
在 Js Event Loop 機制中
Promise 執行器中的代碼會被主線程同步調用,可是 promise 的回調函數是基於微任務的
宏任務的優先級高於微任務
每個宏任務執行完畢都必須將當前的微任務隊列清空
emmmm~~
如今咱們回到上邊的例子中,由於 settimeout 是宏任務,雖然先執行的它,可是他被放到了宏任務的 event queue 裏面,而後代碼繼續往下檢查看有沒有微任務,檢測到 Promise 的 then 函數把它放入了微任務隊列。等到主線進程的全部代碼執行結束後。先從微任務
queue 裏拿回調函數,而後微任務queue空了後再從宏任務的queue拿函數。
因此正確的執行結果固然是:2,4,3,1 ;
由此延申一下 事循環-宏任務-微任務 (Event Queue - Macro - Micro )關係圖:
最後出一個小試題,看看你們是否真的理解到了 Js 的運行機制
試題借鑑 ssssyoki 答案及分析請前往 ssssyoki 博客。
console.log('1'); setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') }) }) process.nextTick(function() { console.log('6'); }) new Promise(function(resolve) { console.log('7'); resolve(); }).then(function() { console.log('8') }) setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') }) })