不求谌解

不求谌解

💻 Web Dev / Creative 💗 ⚽ 🎧 🏓
twitter
github
jike
email

再探js執行機制

image

寫在前面#

入行前端已兩年有餘了。之前我在一篇文章中寫了 —— 要明白前端領域的變與不變。現在和兩年前一樣,各種框架層出不窮,讓人眼花繚亂。很多時候我也在問自己『這個還需要學嗎』。這樣很容易讓我產生焦慮感。而且在面對新事物時,我經常會感到手足無措。歸根結底還是基礎知識不扎實,內功不夠。這也是我寫這篇文章的目的 —— 提升內功,更好的理解『變與不變』。

要開始了#

先從這段程式碼開始探索之旅吧

console.log('開始了')

// setTimeout1
setTimeout(function () {
  console.log('timeout1')
  // promise1
  new Promise(function (resolve) {
    console.log('timeout1_promise')
    resolve()
  }).then(function () {
    console.log('timeout1_then')
  })
}, 2000)

for (var i = 1; i <= 5; i++) {
  // setTimeout2
  setTimeout(function () {
    console.log(i)
  }, i * 1000)
  console.log(i)
}

// promise2
new Promise(function (resolve) {
  console.log('promise1')
  resolve()
}).then(function () {
  console.log('then1')
})

// setTimeout3
setTimeout(function () {
  console.log('timeout2')
  // promise3
  new Promise(function (resolve) {
    console.log('timeout2_promise')
    resolve()
  }).then(function () {
    console.log('timeout2_then')
  })
}, 1000)

// promise4
new Promise(function (resolve) {
  console.log('promise2')
  resolve()
}).then(function () {
  console.log('then2')
})

開始之前,喜歡思考問題的小夥伴可能會有兩個問題。javascript 為什麼被設計為單線程語言,為什麼又會有同步任務和異步任務的區分。這兩個問題,在這裡我就不細述了。感興趣的同學請看阮老師的這篇文章
現在我們來看一下 js 大致是怎麼執行的。

Event Loop

到底哪些是宏任務,哪些是微任務呢。大體上這樣區分。

宏任務(macro-task)

  • script (整體 JavaScript 程式碼)
  • setTimeout()
  • setInterval()
  • setImmediate()
  • I/O
  • UI render

微任務(micro-task)

  • promise
  • async/await (同☝)
  • process.nextTick
  • MutationObserver

預備知識已經準備的差不多了,現在我們開始執行上面那段程式碼。

  • 首先整體 script 進入主線程,遇到console.log(),立即輸出『開始了』
  • 接下來遇到setTimeout,2s 後回調函數 function () 被分發到宏任務 Event Queue 中(注意這裡不是 2s 後執行),這裡標記為 setTimeout1
  • 遇到for,直接執行,同理setTimeout中的回調函數被分發到宏任務 Event Queue 中(這裡涉及到閉包的知識),標記為 setTimeout2,然後執行console.log,輸出『1,2,3,4,5』
  • 遇到promise,new Promise直接執行,輸出『promise1』。then被分發到微任務 Event Queue 中,標記為 then1
  • 接下來又遇到了一個setTimeout,1s 後回調函數 function () 被分發到宏任務 Event Queue 中,標記為 setTimeout3
  • 又遇到promise,同理,輸出『promise2』,then被分發到微任務 Event Queue 中,標記為 then2

第一輪事件循環的宏任務已經執行完畢。Event Queue 中的任務如下表所示

宏任務微任務
setTimeout1 (2s 後)then1
setTimeout2 (1s 後)then2
setTimeout3 (1s 後)

根據上面的流程圖,宏任務執行完後,js 引擎的監視進程會檢查此時有沒有可以執行的微任務。這時後分發到微任務 Event Queue 的then將被執行。依次輸出『then1』,『then2』

第一輪事件循環全部執行完畢。

好了,現在開始第二輪事件循環 (1s 後)。

  • 遇到setTimeout2, 輸出『6』,沒有可以執行的微任務。執行新的宏任務。
  • 遇到setTimeout3, 輸出『timout2』,new promise立即執行,輸出『timeout2_promise』,then被分發到微任務 Event Queue 中。標記為 then3
    第二輪事件循環的宏任務執行完畢。Event Queue 中的任務如下表所示
宏任務微任務
setTimeout1 (1s 後)then3
setTimeout2 (1s 後)

同理,宏任務執行完後。執行此輪的微任務 then3。

第二輪事件循環全部執行完畢。

  • 遇到setTimeout1,執行console.log,輸出『timeout1』,new promise立即執行,輸出『timeout1_promise』,then被分發到微任務 Event Queue 中。標記為 then4
  • 第三輪事件循環宏任務執行完畢,執行此輪的微任務then,輸出『timeout1_then』

第三輪事件循環執行完畢。

  • setTimeout2中依次會產生 4 個宏任務,每隔 1s 輸出一個 6

至此,整段程式碼全部執行結束。

總結:宏任務執行完了,執行該宏任務產生的微任務。如果微任務在執行過程中產生新的微任務,則繼續執行微任務。微任務執行完畢後,回到宏任務中開始下一輪循環。

async#

node#

參考資料#

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。