---
# System prepended metadata

title: 'Promise, Async / Await'

---

# Promise, Async / Await 
## 前言： 程式碼到底是怎麼跑的？
![](https://i.imgur.com/dPeIO0F.png)

#### 【1】Execution Context 執行環境
> 所有程式碼都必須在環境（Execution Context）中執行，而執行環境又分兩種：
- Global Execution Context 執行全域環境（一開始）
- Function Execution Context 執行函式環境（當函式被呼叫）
> 每個環境的執行都會經歷兩個階段：
> - Creation Phase 創造階段
> ![](https://i.imgur.com/aHUq5e7.png)
> - Execution Phase 執行階段
> 一行一行讀取程式碼，如果在這個環境下找不到需要的參數，就會繼續往外層、往全域一層一層的去找，如果正常的執行了，那麼這個執行環境就會跳出 (pop off) 執行堆疊 (stack) 中。
![](https://miro.medium.com/max/1400/1*nMAYYF1N04SjoRfdRmp4NA.gif)
:::spoiler 詳細創造環境流程
![](https://i.imgur.com/RNdC1z8.png)
:::
#### 【2】Execution stack 執行堆疊
>- JavaScript 是單線程（single threaded runtime）的程式語言，所有的程式碼片段都會在堆疊（stack）中被執行，而且一次只會執行一個程式碼片段（one thing at a time）。
>- 他會優先處理最上層的環境，而正在執行中的環境又被稱為 現行環境 (Active context)。
>- 執行到每一個函式中的 return 或結尾時，這個函式便會跳離（pop off）堆疊。

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


#### 【!】Blocking 阻塞
> - 當執行程式碼片段需要等待很長一段時間，或好像「卡住」的一種現象，以網站為例，當堆疊被阻塞（stack blocked），會使瀏覽器無法繼續渲染頁面，導致頁面「停滯」。
> - 為了要解決堆疊被阻塞的問題，我們會使用非同步的方法。
:::info
**Asynchronous 非同步**
雖然 JavaScript的執行環境（Runtime）一次只能做一件事，但瀏覽器提供了更多不同的 API 讓我們使用，進而讓我們可以透過 event loop 搭配非同步的方式同時處理多個事項。
:::

#### 【!】Event Queue 事件佇列（瀏覽器提供）
> 專門用來存放這些非同步的函式，等到整個主執行環境運行結束以後，才會開始依序執行事件儲列裡面的函式。（Event Queue 與 Stack 執行順序相反，先進來的會先被執行）

#### 【!】Event Loop 事件循環（瀏覽器提供）
> 事件循環（event loop）的作用很簡單，如果堆疊（stack）是空的，它便把佇列（queue）中的第一個項目放到堆疊當中；堆疊（stack）便會去執行這個項目。



:::warning
困擾的開始：非同步的流程控制
:::
```javascript=
function delayAdd(n1,n2,delayTime){
    window.setTimeout(function(){
        return n1+n2
    },delayTime)
}
function test(){
    let result=delayAdd(3,4,2000)
    console.log(result)
    }
test() //undefinded🥺
```
## Asynchronous Callbacks 非同步回呼
> 回呼函式常常被使用在非同步的流程完成後需執行的動作，該情境被稱為「 asynchronous callbacks 」。
#### 補充： Callback 回呼函式
>函式中傳入的參數為函式的形式時，該函式參數即為「 回呼函式 」。
>A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.
### 常見非同步回呼
- **計時器**
```javascript=
// setTimeout: 1秒後執行callback函式
window.setTimeout(callback,100)

// setInterval： 每1秒執行一次callback函式
window.setInterval(callback,100)
```

- **事件處理器**
```javascript=
button.addEventListener("click",callback)
```
- **網路請求**
```javascript=
// XMLHttpRequest
function getCurrentVersionNumber(versionCallback){
    // 發送網路請求
    let request = new XMLHttpRequest();
    request.open("GET","http://www.example.com");
    request.send();
    // 請求成功
    request.omload=function(){
        if (request.readyState === 4){
            if(request.status===200){
            let currentVersion = parseFloat(request.responseText)
            versionCallback(null,currentVersion)
            }
        else{
            versionCallback(response.statusText,null)
            }
        }
    }
    // 請求失敗
    request.onerror = request.ontimeout=function(e){
        versionCallback(e.type,null)
    }
}
```
**`request.readyState`： 處理階段**
>０：尚未讀取
>１：讀取中
>２：已下載完畢
>３：資訊交換中
>４：處理完畢

**`request.status`: HTTP協定的狀態碼**
> 200：連線成功
> 404：連線錯誤

### 😈 Callback Hell 回呼地獄
- 巢狀結構的 callback function 語法。（一個callback裡面還有其他callbacks）
- 為了強制流程的順序，需大量使用判斷式。
- 傳入過多的 CallBack function。

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

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

## ES6 Promise 物件
> 表示「 單一 」非同步運算的最終完成或失敗的物件。
> （因此不會在將Promise用於按鈕事件等等，需重複觸發的情境）
- 語法結構較為線性（Promise Chain），容易閱讀。
- 標準化錯誤的處理方式。
- 串接Promise的技巧：Promise.all, Promise.race。
```javascript=
// callback寫法
function delayAdd(n1,n2,delayTime,callback){
    window.setTimeout(function(){
        callback(n1+n2)
    },delayTime)
}
function test(){
    delayAdd(3,4,2000,function(result){
        console.log(result)
    })
}

// Promise物件
function delayAdd(n1,n2,delayTime){
    return new Promise(function(resolve,reject){
    window.setTimeout(function(){
        resolve(n1+n2)
    },delayTime)
}
function test(){
    let promise=delayTime(3,4,2000)
    promise.then(function(result){
        console.log(result)
    })
}
```
## ES7 async / await
>- async / await 是一個簡化Promise的語法糖。
>- async 用來宣告一個非同步函式，讓這個函式本體是屬於非同步，但以看似同步的方式運行程式碼。
>- await 則可以暫停非同步函式的運行（中止 Promise 的運行），直到非同步流程結束。

- async（function）,await（運算子） 會一起被使用。
- 使用 async 宣告的函式必須為Promise物件
- 由於宣告為 async 的函式會被視為一個同步函式，所以當發生錯誤時，程式會全部停止運作！因此通常會搭配   try...catch 使用。


```javascript=
function delayAdd(n1,n2,delayTime){
    return new Promise(function(resolve,reject){
    window.setTimeout(function(){
        resolve(n1+n2)
    },delayTime)
}
async function test(){
    try{
        let result = await delayTime(3,4,2000) //7
        console.log(result)
    }
    catch(error){
        console.log("error",error)
    }
    })
}
```

### 資料參考
[回呼函式 Callbacks、Promises 物件、Async/Await 非同步流程控制 - 彭彭直播](https://www.youtube.com/watch?v=NOprCnnjHm0&ab_channel=%E5%BD%AD%E5%BD%AD%E7%9A%84%E8%AA%B2%E7%A8%8B)
[MDN Promise](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Using_promises)
[秒懂！JavaSript 執行環境與堆疊](https://medium.com/%E9%AD%94%E9%AC%BC%E8%97%8F%E5%9C%A8%E7%A8%8B%E5%BC%8F%E7%B4%B0%E7%AF%80%E8%A3%A1/%E6%B7%BA%E8%AB%87-javascript-%E5%9F%B7%E8%A1%8C%E7%92%B0%E5%A2%83-2976b3eaf248
)
https://hsiangfeng.github.io/javascript/20190530/3821927811/
https://itbilu.com/javascript/js/41KMSZ9a.html
https://cythilya.github.io/2018/10/31/promise/
https://pjchender.blogspot.com/2017/08/javascript-learn-event-loop-stack-queue.html