常規 Event Loop
這篇文章展示了給定的 JS 片段的呼叫堆疊,事件迴圈,和瀏覽器環境給出的非同步 API 三者之間的關係和執行順序。
---------------------------------------------------------------------
給出的 JS :
setTimeout(() => {
console.log('hi')
}, 1000)複製程式碼
呼叫堆疊,事件迴圈和 Web APIs 存在如下關係:
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | | | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |複製程式碼
起初,這三者都是空的。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | <global> | | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |複製程式碼
程式碼開始執行,並將一個 <global> 推進 Call Stack。注:<global> 可以理解為全域性變數所處的執行環境。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
> setTimeout(() => { | <global> | | | |
console.log('hi') | setTimeout | | | |
}, 1000) | | | | |
| | | | |複製程式碼
第一行程式碼執行完畢,將會把函式執行作為第二項推進 Call Stack。值得注意的是 Call Stack 是一個堆疊,它遵循後進先出原則--推進去的最後一個元素將會成為彈出的第一個元素。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
> setTimeout(() => { | <global> | | | timeout, 1000 |
console.log('hi') | setTimeout | | | |
}, 1000) | | | | |
| | | | |複製程式碼
執行 setTimeout 函式實際上呼叫了 JS 之外的程式碼。setTimeout 屬於 Web API 的一部分,而 Web API 是由瀏覽器環境提供的。當然了,node 中也存在一套不同的 API。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | <global> | | | timeout, 1000 |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |複製程式碼
然後,setTimeout 語句執行完畢,它將工作交付 Web API 。Web API 將會等待指定的時長(1000ms)。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | | | | timeout, 1000 |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |複製程式碼
此時,這裡沒有其他的 JS 需要執行了,Call Stack 現在是空的狀態。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | | function <-----timeout, 1000 |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |複製程式碼
一旦時延到達指定時長(1000ms),Web API 將會把程式碼加入到 Event Loop (事件迴圈)內,從而使得 JS 能夠執行。
這裡需要注意的是 Web API 並非直接將所要執行的程式碼推到 Call Stack中,因為這樣做可能打斷正在執行的程式碼,那會得到一個非常詭異的結果。
Event Loop 是一個佇列。第一個被推進來的元素也會被首先推出去。遵從先入先出原則。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | function <---function | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |複製程式碼
當 Call Stack 內沒有程式碼執行的時候, JS 執行環境將檢查 Event Loop 內是否有等待執行的任務在排隊。如果有,第一個元素將會被移動到 Call Stack 來執行。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | function | | | |
> console.log('hi') | console.log | | | |
}, 1000) | | | | |
| | | | |複製程式碼
箭頭函式執行的結果就是呼叫了 console.log 。那麼這句程式碼將被推到 Call Stack 內。(因為這是同步的API)
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | function | | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |
> hi複製程式碼
console.log 執行完畢後,列印出 hi。Call Stack 同時移除這部分程式碼。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | | | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |
> hi複製程式碼
最後,箭頭函式內沒有其他的指令需要執行,它也被從 Call Stack 中移除。
我們的程式就此結束了執行。
End.
---------------------------------------------------------------------原文連結:Regular Event Loop
有任何建議或疑惑,歡迎在評論區留言。