---
# System prepended metadata

title: 'JavaScript — Heap, Stack, Event-loops and Callback Queue'

---

# JavaScript — Heap, Stack, Event-loops and Callback Queue


* 網頁的運行
* 名詞解說
* 實際測試：分解程式碼
* 關於setTimeout的毫秒數
## 
### 網頁的運行
簡單提及網頁的運行方式，
其中最重要的是關於JavaScript的兩種特性，分別為單線程語言(single thread)和同步執行(synchronous)。
這兩種特性說明了Javascript的運行環境會由上到下的執行程式，並且一次只處理一件事，做完一件事才接著做下一件。

>那這樣我們的網頁是如何處理繁複的資料與程式，並呈現到網頁上的呢？


因為瀏覽器不僅僅只有Javascript的運行環境，它提供了許多webAPIs，我們會透過程式的呼叫來做運用，舉例來說，像是常見的`setTimeout`,`ajax(XMLHttprequest)`，當瀏覽器接收到這些代碼時，就會被移動到webAPIs執行，而不會阻擋到主程式。也就是達到非同步執行程式（asynchronous）的效果。

>所以說這個非同步實際上是如何被運行的呢？讓我們接著看下去 ...

### 名詞解說

<font color=red>`Heap`</font>：是一個拿來存放程式中已宣告之變數、函式等的空間。
<font color=red>`Stack`</font>：程式中要執行之函式跳進去的地方，執行完即跳出，具有後進先出(Last in First out)的性質。
<font color=red>`WebAPIs`</font>：當要執行非同步程式時，瀏覽器會在這邊開始執行，不會影響到主程式，執行完畢後不會直接被送到Stack，而是被送到callback Queue排隊等待。
<font color=red>`Callback Queue / Task Queue`</font>：是儲放非同步程式執行完成的空間，等待被丟進Stack執行。
<font color=red>`Event Loop`</font>：Event Loop 會持續查看 Stack 是否都執行完空下來了，如果是的話就會把 Callback Queue 裡面的任務依序丟進 Stack 去做執行。

將以上的流程以圖像化呈現，大致上是這樣子的

![](https://i.imgur.com/fCNHOhK.png)
##

### 實際測試：分解程式碼

這邊以一段簡短的程式碼作為實例

![](https://i.imgur.com/7pJZ49r.png)

程式執行的結果會是

![](https://i.imgur.com/l1Tw40e.png)

>這五秒間背後的程式是如何被運作的呢？這裡我使用[Loupe](http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D)視覺化工具進行說明。

<br/>

1.首先會執行`console.log("Hi There.")`。

![](https://i.imgur.com/aRqT4SA.png)


2.瀏覽器偵測到`setTimeout`，於是將它移動到webAPIs做執行，主程式繼續往下執行。

![](https://i.imgur.com/maz1LXr.png)


3.執行`console.log("Hello again.")`。

![](https://i.imgur.com/i8Fe2nG.png)


4.五秒過後setTimeout執行完成，移動至Callback Queue 裡等待傳喚 

![](https://i.imgur.com/l9wPglW.png)



5.Event Loop發現Stack裡任務皆被清空，會呼叫Callback Queue裡面的任務丟進Stack裡，執行`console.log("passed 5 sec.")`。
![](https://i.imgur.com/ZiNl2jv.png)


[點擊這裡可以看在Loupe上執行的效果](http://latentflip.com/loupe/?code=Y29uc29sZS5sb2coIkhpIFRoZXJlLiIpCgpzZXRUaW1lb3V0IChmdW5jdGlvbiBjYWxsYmFjaygpewogICAgY29uc29sZS5sb2coInBhc3NlZCA1IHNlYy4iKQp9LDUwMDApCgpjb25zb2xlLmxvZygiSGVsbG8gYWdhaW4uIik%3D!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D)

將流程視覺化後可以顯而易見的了解到，當程序中出現非同步程式時會被隔離到webAPIs執行，就不會擋住主程式，避免掉同步執行時會有的阻塞(block)現象。經過圖像化的說明後，是不是變得比較好理解了呢？



### 關於setTimeout的毫秒數

另外`settimeout`裡面所設定的毫秒數只能代表他==至少==需要多少時間，就像前面所說的，程式執行完成後會進到`Callback queue`等待`EventLoop`偵測當前`Stack`是否為空，才會依序做執行。

假如今天有一段代碼如下：

![](https://i.imgur.com/fvqydmU.png)

執行後的結果會是這樣

![](https://i.imgur.com/pamjrSX.png)

由此可說明setTimeout只能確保幾毫秒過後程式==即將==會被執行，並不表示幾毫秒過後它會即刻被執行。





[點擊這裡可以看在Loupe上執行的效果](http://latentflip.com/loupe/?code=c2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coJzFzZWMgbGF0ZXI%2FJykKfSwgMTAwMCkKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coJzFzZWMgbGF0ZXI%2FJykKfSwgMTAwMCkKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coJzFzZWMgbGF0ZXI%2FJykKfSwgMTAwMCkKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coJzFzZWMgbGF0ZXI%2FJykKfSwgMTAwMCk%3D!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D)


## 

### 參考資料
[MDN-webAPIs](https://developer.mozilla.org/zh-TW/docs/Web/API)
[Philip Roberts: What the heck is the event loop anyway?](https://www.youtube.com/watch?v=8aGhZQkoFbQ&ab_channel=JSConf)
[JavaScript、React、Flask、MongoDB 網站全端開發：從入門到進階](https://www.udemy.com/course/javascript-es6-react-redux/)

