# Promise ## 今日上課知識點 - 複習同步與非同步 - Callback Function - Promise ## 同步與非同步 同步: ```javascript function A(){ console.log('a'); } function B(){ console.log('b'); }; A(); B(); ``` 非同步: ```javascript! function A(){ setTimeout(() => { console.log('a'); },1000); } function B(){ console.log('b'); }; A(); B(); ``` ## Callback Function 早期用 Callback Function 來處理非同步事件,來控制操作順序 ```jsx function A(B){ setTimeout(() => { console.log('a'); B(); },1000); } function B(){ console.log('b'); }; A(B); ``` 常見的 Callback Function 應用 ```jsx buttonElement.addEventListener("click", { handleEvent: function (event) { alert("Element clicked through handleEvent property!"); }, }); ``` 可以用 callback function 的形式去確認所有的非同步操作順序 ```jsx asyncOperation1(() => { asyncOperation2(() => { asyncOperation3(() => { console.log("完成所有非同步操作"); }); }); }); ``` 保證先執行 `asyncOperation1`,再執行 `asyncOperation2`,最後執行 `asyncOperation3`。 . . . . . <img src="https://hackmd.io/_uploads/H1gKA-VNR.png" width="350"> 多層嵌套的結構導致難理解程式碼、不好閱讀、不好維護,被稱作 Callback Hell Callback Function 比較適合處理簡單的非同步操作 ## Promise Promise 是通過 `new Promise` 建立的,並傳入一個執行函式(executor function),該函式接受兩個參數:`resolve` 和 `reject`。 `resolve` 用來處理合法的值 `reject` 用來處理失敗的值 ```jsx // 使用方法 const promise = new Promise(function(resolve, reject) { console.log('resolve', resolve) console.log('reject', reject) }); ``` Promise 執行時,帶有**其中一種**狀態 - pending (等待中,尚未有結果) - fulfilled (已實現,不會再變成其他狀態)代表這個非同步的結果是成功 - rejected (已拒絕,不會再變成其他狀態)代表這個非同步的結果是失敗 ![18_ironman_cover_26](https://hackmd.io/_uploads/Sk7DOW5EA.jpg) > 附圖來自[卡斯伯文章](https://https://www.casper.tw/javascript/2017/12/29/javascript-proimse/) 當 Promise 被建立時,它的狀態是 pending 當調用 `resolve` 後,狀態會變為 fulfilled 當調用 `reject` 後,狀態會變為 rejected ```jsx // value 是真值,調用 resolve // value 是假值,調用 rejected function asyncFunc(value) { return new Promise(function(resolve, reject) { if (value) { resolve(value); // 成功時觸發 resolve(value),promise 物件的狀態會是 fulfilled } else { reject("操作失敗"); // 失敗時觸發 reject(reason),promise 物件的狀態會是 rejected } }); } asyncFunc(true); // Promise {<fulfilled>: true} asyncFunc(false); // Promise {<rejected>: '操作失敗'} ``` **then, catch 與 finally** then 代表「然後」、「接著」、「接下來」 `then` 方法用來處理 Promise 成功的結果 它接受兩個參數: 第一個是處理成功結果的函式,第二個是處理失敗原因的函式(可選) 通常只使用第一個參數,處理錯誤會在 `catch` 方法做 ⇒ 當 Promise 的狀態轉為 fulfilled 或 rejected 時會執行的 function ```jsx const promise = new Promise(function(resolve, reject) { resolve(true) }); promise.then( result => { console.log("成功:" + result); // 處理成功結果 }, error => { console.log("失敗:" + error); // 處理失敗原因 } ); // 成功:true ``` catch:專門用來處理 Promise 的錯誤。作用等同於在 **`then`** 方法中只提供第二個參數 ⇒ 當 Promise 的狀態轉為 rejected 時會執行的 function ```jsx promise.catch(error => { console.log("失敗:" + error); }); ``` finally:`finally` 方法在 Promise 結束後(無論成功或失敗)都會執行 通常用來執行清理操作 ```jsx promise .then(result => { console.log("成功:" + result); }) .catch(error => { console.log("失敗:" + error); }) .finally(() => { console.log("操作完成"); }); ``` ## Promise 串接 (Chaining) 因為 then 會回傳一個新的 Promise 物件,所以能將多個 **`then`** 方法串接起來,做到以下的連鎖(chain)語法結構 ```jsx const promise = new Promise(function(resolve, reject) { resolve(1) }); promise .then(function(value){ console.log(value) // 1 return value + 1 }) .then(function(value){ console.log(value) // 2 return value + 2 }) .catch(function(error){ console.log('error', error) }) // Promise {<fulfilled>: 4} // 不會執行 catch ``` ## Promise 的進階用法 有時我們需要並行執行多個非同步操作並在所有操作完成後處理結果 例如:讀取多張圖片或資源時,確保所有圖片都載完後再呈現 **Promise.all() 並行執行** 如果有一個 Promise 被拒絕,則整個 `Promise.all` 被拒絕 ```jsx Promise.all([1,2,3]).then((value) => { console.log(value) }) // [1, 2, 3] ``` 有時我們需要在多個 Promise 中,只取最先完成的 Promise 結果(不論是已實現還是被拒絕) **Promise.race() 並行執行,只會取得跑最快的值** ```jsx Promise.race([1,2,3]).then((value) => { console.log(value) }) // 1 ``` ## **Promise 的實際應用:setTimeout** ```jsx setTimeout(() => { console.log("非同步操作完成"); }, 2000); console.log("我想等 setTimeout 執行完再出現!") // 我想等 setTimeout 執行完再出現! // 非同步操作完成 ``` 將 setTimeout(非同步事件)包在一個 Promise 物件中。 當這個非同步操作完成時,可以調用 Promise 的 `resolve()` 方法將 Promise 轉為 fulfilled ,再執行後續的程式碼。 ```jsx // 使用 Promise 來處理非同步操作 function asyncOperation() { return new Promise((resolve, reject) => { setTimeout(() => { console.log("非同步操作完成"); resolve(); // 當操作完成時,調用 resolve 方法,將狀態從 pending 轉為 fulfilled }, 2000); }); } // 接著執行 console 內容 asyncOperation().then(() => { console.log("我想等 setTimeout 執行完再出現!"); }); // 非同步操作完成 // 我想等 setTimeout 執行完再出現! ``` ## Promise 的實際應用:API 請求 技術上通常會用 fetch 或是 axios 去寫, fetch 和 axios 兩者都基於 Promise 提供了非同步的請求處理機制 **Promise 用在 axios** ```jsx axios.get('https://api.example.com/data') .then(function (response) { // 請求成功 console.log(response.data); }) .catch(function (error) { // 請求失敗 console.error(error); }); ``` axios.get() 返回的是一個 Promise 物件 - 當請求成功時,then 方法被調用,並在其中處理 response data - 當請求失敗時,catch 方法被調用,處理錯誤 ## async await ```jsx async function getData(){ try { const response = await axios.get('https://api.example.com/data'); console.log(response.data); } catch (error) { console.error(error); } } ```