Skip to content

Latest commit

 

History

History
85 lines (65 loc) · 3.5 KB

File metadata and controls

85 lines (65 loc) · 3.5 KB

hw2:Event Loop + Scope

在閱讀之前,請先參考 hw5 筆記內容認識 Scope、Call Stack、Web APIs、Event Loop 等名詞,這樣才可以理解以下的步驟。

for(var i=0; i<5; i++) {
  console.log('i: ' + i)
  setTimeout(() => {
    console.log(i)
  }, i * 1000)
}
1. 宣告 i 為全域變數

使用 var 來宣告 i 的地方,因為不在 function 裡面,i 變成全域變數,任何地方都可以存取到它。

跑每一次迴圈要執行以下的程式碼,總共要跑五次

console.log('i: ' + i)
  setTimeout(() => {
    console.log(i)
  }, i * 1000)
2. console.log('i: ' + 0) 任務進入到堆疊中執行,印出 i: 0 (此時 i 為 0)
3. 第一次迴圈的 setTimeout() 進入堆疊(此時 i 為 0)

setTimeout() 不存在於 JavaScript 原始碼內,它屬於瀏覽器提供的 Web APIs 一種,所以瀏覽器會把它移到其他的執行緒執行,因為此時的 i 是 0,瀏覽器設置為 0 毫秒計時(0 * 1000),而 JS 則繼續執行。

4. i++(此時 i 為 1)

因為 i 小於 5,進入第二次迴圈。

5. console.log('i: ' + 1) 任務進入到堆疊中執行,印出 i: 1 (此時 i 為 1)
6. 第二次迴圈的 setTimeout() 進入堆疊(此時 i 為 1)

第二次迴圈的 setTimeout() 進入到堆疊,同樣因為是 Web APIs 而被 JS 忽略,瀏覽器將它移到其他的執行緒,不影響 JS Runtime 執行。此時的 i 是 1,瀏覽器設置為 1 秒計時(1 * 1000),而 JS 則繼續執行。

7. i++(此時 i 為 2)

因為 i 小於 5,進入第三次迴圈。

8. console.log('i: ' + 2) 任務進入到堆疊中執行,印出 i: 2 (此時 i 為 2)
9. 第三次迴圈的 setTimeout() 進入堆疊(此時 i 為 2)

瀏覽器將它移到其他的執行緒,設置為 2 秒計時(2 * 1000),而 JS 則接著繼續執行。

10. i++(此時 i 為 3)

因為 i 小於 5,進入第四次迴圈。

11. console.log('i: ' + 3) 任務進入到堆疊中執行,印出 i: 3 (此時 i 為 3)
12. 第四次迴圈的 setTimeout() 進入堆疊(此時 i 為 3)

瀏覽器將它移到其他的執行緒,設置為 3 秒計時(3 * 1000),而 JS 則繼續執行。

13. i++(此時 i 為 4)

因為 i 小於 5,進入第五次迴圈。

14. console.log('i: ' + 4) 任務進入到堆疊中執行,印出 i: 4 (此時 i 為 4)
15. 第五次迴圈的 setTimeout() 進入堆疊(此時 i 為 4)

瀏覽器將它移到其他的執行緒,設置為 4 秒計時(4 * 1000),而 JS 則繼續執行。

16. i++(此時 i 為 5)

因為 i 沒有小於 5,結束迴圈,堆疊被清空。

17. 回調

上面那些 console.log('i: ' + i) 其實在程式運行之初就跑完了,剩下的就是等待。

那些被瀏覽器移到其他執行緒的 setTimeout(),是交由瀏覽器提供的執行緒計時,每次計時完裡頭的 callback function () => { console.log(i) } 就會按 0、1、2、3、4 秒被移到 Callback Queue(回調序列),等待 Event Loop 的安排。

Event Loop 一發現堆疊清空了,也就是 i 變成 5 迴圈執行完畢,就會將第一個 () => { console.log(i) } 移到 stack 中執行,呼叫 console.log(i),因為 i 原本就是全域變數,之前每一次的賦值其實都是改到同一個變數,所以印出的 i 都是 5。

執行結果為

i: 0
i: 1
i: 2
i: 3
i: 4

5
// 1s
5
// 1s
5
// 1s
5
// 1s
5