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