---
# System prepended metadata

title: 什麼是 Promise？    async/await 怎麼用？

---

> 此篇內容節錄並改寫自 [使用 Promise 處理非同步](https://ithelp.ithome.com.tw/articles/10194622)

# 什麼是 Promise？    async/await 怎麼用？

Promise 是 JavaScript 中進行非同步操作的新的解決方案

+ Promise 是一個建構式
+ Promise 物件用來封裝一個非同步操作，再使用 `then()` 拿到結果
+ ES7 `await` 語法更方便簡單

## 為什麼要使用 Promise？

解決回呼地獄的問題

+ chaining（鏈式）：若在 `.then()` 方法中 return 一個 Promise，可以再用 `.then` 去接收上一個 Promise resolved 的值；若在 `.then()` 方法中 return 一個不是 Promise 的值，下一個 `.then()` 接收的值就是上一個 `.then()` 方法 return 的值本身。

## Promise 的狀態與流程

Promise 中主要有以下幾個狀態，從一進入 Promise 就進入 pending (等待事件完成)，接下來會依據事件的成功與否回傳成功或拒絕的結果，若事件成功就呼叫 resolve 函式、失敗則呼叫 reject 函式來傳遞成功與失敗的訊息。


+ pending: 等待中的初始狀態
+ fulfilled: 正確完成
+ rejected: 已拒絕，操作失敗

## 基本使用範例

``` javascript
// 1. 創建一個 Promise 物件
        const p = new Promise((resolve, reject) => 
            // 2. 執行非同步操作
            setTimeout(() => {  // 如果現在時機為偶數代表成功，否則代表失敗
                const time = Date.now()
                // 3. 如果成功，呼叫 resolve 函式
                if (time % 2 === 0) {
                    resolve(`成功了！ time=${time}`)
                } else {
                    // 3. 如果成功，呼叫 reject 函式
                    // reject(`失敗了！ time=${time}`)
                }
            }, 2000)
        })
        p.then(
            (value) => {  // 接收 resolve 成功的結果：onFulfilled
                console.log(value);
            },
            // (reason) => { // 接收 reject 失敗的結果：onRejected
            //     console.log(reason);
            // }
        )
        // 接收 reject 失敗的結果：onRejected，與上面 then 第二個參數相同擇一使用即可
        // .catch((err) => { 
        // console.log(err)
        // })
```

## `async` 和 `await` 語法糖

`async` 和 `await` 為 Promise 的簡單寫法（用同步的方式寫非同步程式碼）
範例：

``` javascript
async function getData() {
  try {
    const response = await fetch(api);
    const data = await response.json();
    console.log(data);
  } catch (err) {
    console.log(err);
  }
}

getData();
```

+ `await` 用來等待一個 Promise，並只能在 async function 內使用，會回傳 Promise 物件的 resolved 值。若 await 的值不是一個 Promise 物件就回傳該值本身。
+ `await` 的用法是等待完一個 Promise 之後直接在宣告一個變數來承接結果即可。

+ 除錯直接用同步的 `try` and `catch` 方法

立即函示寫法：
```javascript=
const axios = require('axios');
const fs = require('fs/promises');

(async function () {
  try {
    const stockText = await fs.readFile('./stock.txt', 'utf-8');
    const response = await axios.get(
      'https://www.twse.com.tw/exchangeReport/STOCK_DAY',
      {
        params: {
          // 設定 query string
          response: 'json',
          date: '20220301',
          stockNo: stockText,
        },
      }
    );
    console.log(response.data);
  } catch (err) {
    console.log(err);
  }
})();

```
### 補充
#### Async function 中 return 任何值都會是一個 Promise！
```javascript=
async function test() {
  const data = await new Promise((resolve, reject) => {
    let bar = true;
      if (bar) {
        resolve('123');
      } else {
        reject();
      }
    });
    console.log(data); // 2. 後印出 123
    return 1;
  }
const result = test();
console.log(result);  // 1. 先印出 Promise {<pending>}
```
##### 注意
程式執行到 `await` 時便會暫停 **`await` 關鍵字以下、該 async funtion 內的程式碼**。
承上題，執行 `test()` 後第 2 行至第 12 行的程式都會被暫停，接著執行第 14 行：`console.log(result)`，此時的 result 是一個 pending Promise，儘管我宣告 test async function 時最後是 return 數值 1，因為不管如何在 async function return 任何值，它都會變成一個 Promise！

#### 試題練習
1.
```javascript=
// 沒有 await
function testFun() {
  console.log(1);

  new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(2);
      resolve();
    }, 0);
  });
  console.log(3);
}
console.log(4);
testFun();
console.log(5);
```
Output to be 4 -> 1 -> 3 -> 5 -> 2

2.
```javascript=
// 有 await
async function asyncF() {
  console.log(1);
  // await 暫停鍵
  await new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(2);
      resolve();
    }, 0);
  });
  console.log(3);
}
console.log(4);
asyncF();
console.log(5);
```
Output to be 4 -> 1 -> 5 -> 2 -> 3