# JavaScript - Macrotask 與 MicroTask 差異以及情境使用


提到 Macrotask 與 MicroTask 之前，先再次複習 Promise 的觀念：

1. 一個 Promise 最終會有兩種狀態
2. 對應 Promise 的不同狀態，會各自觸發 .then 與 .catch 兩個函式
3. 利用 Promise 可以達成非同步行為，而且內容可以自訂

## Macrotasks

關於 Event Queue 有些 Web API 是具有非同步的行為，而在非同步的目的達成之後，瀏覽器會把給定的對應的函式推送到 Event Queue 裡面，這些一個一個函式正好代表每一件要做的事情，因此在 JS 裡面，以「 Task 」或 「Macrotask 」來稱呼。

![Macrotask 執行情境](https://d1dwq032kyr03c.cloudfront.net/upload/images/20190930/20106580UMeCNMZgKH.jpg "Macrotask 執行情境")

關於 Task 有兩個細節可以注意：

- 以瀏覽器的角度來看，在每一個 Task 結束之前，不會有任何瀏覽器的 rending 產生
- 如果一個 Task 執行所花的時間過長，那麼瀏覽器就無法執行其他的 Task ，所以過一段時間之後會提出「頁面沒有回應」的警告，建議你關閉這個分頁，這種情況你應該有遇過。

## Microtasks

Microtask 通常由 Promise 產生，Promise 裡用到的 .then / .catch 函式會以非同步的方式來被執行，回想下 Queue 的概念，**非同步行為指的是，會在全域執行環境執行完之後才被執行**，因此只要 Promise 的 callback 內容執行完成，狀態再也不是 pending 時，.then 或 .catch 的函式內容就會被推送到 Queue 裡面等待執行，這個被推送到 Queue 的函式就是 Microtask。

相對於管理 Web API 所屬事件的 Macrotask Queue ，Promise 產生的 Microtask 也有自己的 Queue ，在 JS 內被稱為 Job Queue 或 Microtask Queue，而 Job Queue 與 Event Queue 運作方式上有一點不一樣。

差在哪裡呢？**在 Queue 裡面的每個 Macrotask 執行完畢後 ，就算 Event Queue 裡面還有其他的 Task，JS 引擎依舊會優先執行 Microtask Queue 裡面的所有 Task** ，在這個同時也不會重新渲染網頁，換句話說，Microtask 的執行是穿插在每個 Macrotask 之間，兩者的差異也就在執行順序的不同而已。

![MicroTask 執行情境](https://d1dwq032kyr03c.cloudfront.net/upload/images/20190930/20106580a7zj27GtsT.jpg "MicroTask 執行情境")

## Microtask 與 Macrotask 同時發生的範例

前端面試經常會考到這題，這邊使用過去被考到的範例來做拆解解析

```javaScript
setTimeout(() => alert("timeout"));

Promise.resolve()
  .then(() => alert("promise"));

console.log("Global");
```

這段程式碼剛好同時用到 Web API 與 Promise ，各自在呼叫後會產生一個 Macrotask 以及 Microtask ，不過在順序上是哪個會先被執行呢？

- 所有的 Queue 都會等待執行環境堆疊被清空，alert 肯定會先執行
- setTimeout 對應的函式會被當作一個 Macrotask ，等待時間到之後被送入 Macrotask Queue
- Promise 對應的 .then 或 .catch 的函式會被當作一個 Microtask 送入 Microtask Queue
- 在執行環境堆疊清空之後，通常網頁會先做一次 Render，Render 的動作同時也算是一個 Macrotask

解析內容：
總共執行了三組程式碼，內容有 `console`、`setTimeout`、`Promise` ，而 `setTimeout`、`Promise`第一個時間觀看到就會知道會產生 Event Loop 的情形，自動地將他的執行序往後排，所以優先會先去執行 `console`

1. `"Global"`

接下來我們直接透過 JS 由上執行到下的觀念來判斷到底先執行 setTimeout 還是 Promise

先將答案推測為這樣：

1. `"Global"`
2. `"timeout"`
3. `"promise"`

但是並不是！結果會是 `"promise"` 比 `"timeout"` 還要更先被 log 出來：

1. `"Global"`
2. `"promise"`
3. `"timeout"`

這是為什麼呢？這邊可能會有點抽象，前面我們在分析 JS 語法與運作模式的時候，大多是從 JS 引擎的角度出發。而前面也有提到， Queue 的概念並不屬於 JS 引擎的一部分，相對的歸屬於瀏覽器。對於瀏覽器來說，在網頁頁面開啟時，載入對應的 JS 檔並且執行這件事情，也是一個 Macrotask 。

而剛剛提到 Macrotask 執行完畢後，會優先執行 Microtask ，因此你會看到 `"promise"` 出現的順序先於 `"timeout"` 。

![最後執行的結果](https://d1dwq032kyr03c.cloudfront.net/upload/images/20190930/20106580BZxnDVGnKD.jpg "最後執行的結果")

