一道面試題引起的思考 --- Event Loop

想必面試題刷的多的同窗對下面這道題目不陌生,可以當即回答出輸出10個10,但是你真的懂爲何嗎?爲何是輸出10?爲何是輸出10個10?這兩個問題在我腦邊縈繞。嗯,我得說服本身。html

for (var i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i)
  }, 0)
}

JavaScript 是單線程。(ok,我又問本身它爲何是單線程 ==》做爲瀏覽器語言,JS的用途是與用戶交互以及操做DOM,若是是多線程會引起不少問題,瀏覽器沒法判斷以哪一個線程爲標準,所以它只能是單線程)
任務分爲「同步任務」與「異步任務」。同步任務都好理解,一個執行完執行下一個。異步任務稍許複雜。
異步任務運行機制:java

  • 同步任務在「主線程」上執行,造成一個「執行棧」(execution context stack)
  • 主線程以外,還有一個「任務隊列」(task queue),異步任務有了運行結果,就在任務隊列裏放置一個事件
  • 執行棧中同步任務執行完,就會去讀取任務隊列的事件,異步任務事件結束等待狀態,進入執行棧執行
  • 主線程重複前面三步

其實屢清楚很好理解,以上運行機制又稱爲 Event Loop(事件循環)
咱們回到這道面試題, 一塊兒來理解下:git

for 循環是同步任務,setTimeout 是異步任務
首先 for 循環在主線程上執行,setTimeout 進入任務隊列
同步任務執行完,i = 10,此時 setTimeout 被喚醒進入執行棧執行
所以輸出的值爲10
但是爲何會輸出10個10呢?到如今我仍是沒有徹底說服本身。

咱們對代碼稍做修改,github

for (var i = 0; i < 10; i++) {
  console.log(i)
  var p = setTimeout(() => {
    console.log(i)
  }, 0)
  console.log(p)
}

輸出結果面試

0
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: TimersList, _idleStart: 1893, …}
1
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: Timeout, _idleStart: 3739, …}
2
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: Timeout, _idleStart: 4924, …}
......
9
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: Timeout, _idleStart: 11733, …}
10
10
...
10

看到這個結果你們是否有些清楚了呢,咱們從新梳理下原先的面試題瀏覽器

首先 for 循環在主線程上執行,setTimeout 內部的回調函數進入任務隊列
for 循環裏,i 每次執行一次,異步隊列裏放置一個 setTimeout 回調
同步任務執行完,i = 10, 此時異步隊列裏放置了10個回調事件
setTimeout 被喚醒進入執行棧執行
所以輸出了10個10

固然了, Event Loop 的知識不止這點,涉及到的東西也不少。本文只是我對這道面試題的一點思考,有誤的地方望批評指正。多線程

如下幾篇是我收藏的好文,供你們學習參考~
JavaScript 運行機制詳解:再談Event Loop
這一次,完全弄懂 JavaScript 執行機制
從event loop規範探究javaScript異步及瀏覽器更新渲染時機異步

相關文章
相關標籤/搜索