> 此篇內容節錄並改寫自 [使用 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