# What is the extent of your experience with Promises and/or their polyfills? ### What is the extent of your experience with Promises - Fetch: return 的東西就是一個 promise - 後端用node.js接資料庫:mysql 在送 query 給資料庫時,因為要根據資料庫查詢結果做判斷,所以也做成 Promise 的形式,等待得到查詢結果後,再繼續做下一個判斷,最後再把處理完的結果送給前端 ### Promises polyfills Some common polyfills are `$.deferred`, Q.js and Bluebird but not all of them comply with the specification. ES2015 supports Promises out of the box and polyfills are typically not needed these days. [Promise的实现类库(Library)](https://wohugb.gitbooks.io/promise/content/advanced/library.html) --- # Promise的架構 ## 什麼是 Promise Promise 物件代表一個即將完成、或失敗的非同步操作,以及它所產生的值。 :::info 作為將來未知的回傳值的代理 (A promise serves as a proxy for a future value) ::: ## 為什麼要用 Promise Promise 建構式主要用於包裹尚未支援 Promise 的函式。 ## 建立 Promise Promise 的 constructor 接收一個叫作”執行器函式(executor function)“的引數。此函式接收兩個函式作為引數。 第一個函式(`resolve`)在非同步作業成功完成時,以該作業之結果值被呼叫。 第二個函式(`reject`)在作業失敗時,以失敗訊息,通常是一個 error object,被呼叫。 ```jsx const myFirstPromise = new Promise((resolve, reject) => { //使用 new 建立 Promise 物件 // 執行一些非同步作業,最終呼叫: // // resolve(someValue); // 實現 // 或 // reject("failure reason"); // 拒絕 }); ``` ## 讓一個函式具備 Promise 功能 讓它回傳一個 promise 即可 ```jsx function myAsyncFunction(url) { // myAsyncFunction 函式具備 Promise 功能 return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onload = () => resolve(xhr.responseText); xhr.onerror = () => reject(xhr.statusText); xhr.send(); }); }; ``` ### 一個 `Promise` 物件處於以下幾種狀態: - *擱置(pending)*:初始狀態,不是 fulfilled 與 rejected。 - *實現(fulfilled)*:表示操作成功地完成。 - *拒絕(rejected)*:表示操作失敗了。 當上述狀態轉換(`pending → fulfilled` 或 `pending → rejected`)發生時,那些透過 then 方法所關聯(associated)的處理函式就會依序被調用。 ## 運用 Promise 的回傳值:使用 `then()`、`catch()` - 透過呼叫 `then()` 來決定 `promise` 進入 `resolved` 時,要透過 `then()` 做什麼 - 或是進入 `rejected` 時,要透過 `catch()` 方法要做什麼 ```jsx 'use strict'; var promiseCount = 0; function testPromise() { let thisPromiseCount = ++promiseCount; let log = document.getElementById('log'); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Started (<small>Sync code started</small>)<br/>'); // 建立一個新的 promise:此 promise 承諾一個數值計數, 由 1 開始(等待約 2 秒) let p1 = new Promise( // 這個解決器函數(resolver function)呼叫實現或拒絕 promise。 (resolve, reject) => { log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Promise started (<small>Async code started</small>)<br/>'); // 在此例子單純用來產生非同步特性。 window.setTimeout( function() { // 實現這個 promise! resolve(thisPromiseCount); }, Math.random() * 2000 + 1000); } ); //----------- 上面是建立一個 Promise,下面是運用 Promise 的回傳值 -----------// // 接著透過呼叫 then() 來決定 promise 進入 resolved 時,要透過 then() 做什麼;或是進入 rejected 時,要透過 catch() 方法要做什麼。 p1.then( // 印出實現值(fulfillment value) function(val) { log.insertAdjacentHTML('beforeend', val + ') Promise fulfilled (<small>Async code terminated</small>)<br/>'); }) .catch( // 印出失敗訊息(rejection reason) (reason) => { console.log('Handle rejected promise ('+reason+') here.'); }); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Promise made (<small>Sync code terminated</small>)<br/>'); } ``` ### then() 的結構 `then()` 方法回傳的也是一個 Promise 物件(也可以不回傳)。 > then 方法回傳一個 Promise 而可以進行方法串接(method chaining)。 如果不回傳物件,則這個 Promise 的串接就結束了。 它接收兩個 argument: Promise 在**成功**及**失敗**情況時的回呼函式。 ```jsx promise.then(onFulfilled[, onRejected]); //兩個 argument ``` 所以 then() 是可以用來處理 fulfillment 和 rejection 兩種狀況的。 ```jsx //處理 fulfillment 和 rejection 兩種狀況 promise.then(function(value) { // fulfillment }, function(reason) { // rejection }); ``` ### then() 的方法串接(method chaining) ```jsx Promise.resolve('foo') .then(function(string) { return new Promise(function(resolve, reject) { //then 方法回傳一個 Promise setTimeout(function() { string += 'bar'; resolve(string); }, 1); }); }) .then(function(string) { setTimeout(function() { string += 'baz'; console.log(string); }, 1) return string; //then 方法回傳一個 Promise:Promise.resolve(<value returned by whichever handler was called>) }) .then(function(string) { //then 方法不回傳 console.log("Last Then: oops... didn't bother to instantiate and return " + "a promise in the prior then so the sequence may be a bit " + "surprising"); console.log(string); }); ``` ### rejection 的狀況不一定要用 `then()` 來處理:`catch()` 實務上,使用 catch 是較理想的。不建議使用兩個 argument 的 then 語法。 ```jsx Promise.resolve() .then( () => { throw 'Oh no!'; // Makes .then() return a rejected promise }) .catch( reason => { console.error( 'onRejected function called: ', reason ); }) .then( () => { console.log( "I am always called even if the prior then's promise rejects" ); }); ``` ## 補充 ### `Promise` v.s. `async/await` 的轉換 - Promise: ```jsx= function logFetch(url) { return fetch(url) .then(response => response.text()) .then(text => { console.log(text); }) .catch(err => { console.error('fetch failed', err); }); } ``` - Async/await: ```jsx= async function logFetch(url) { try { const response = await fetch(url); //promise 的 resolve(): 第一個 then() console.log(await response.text()); //promise 的 resolve(): 第二個 then() } catch (err) { console.log('fetch failed', err); } } ``` ```jsx= const test = async function(url){ const result = await logFetch(url); //等待一個結果 //do something } test(); //可以不用加async async function show(){ await test(); //等待test回傳的結果 //用那個結果 do something } ``` - 參考資料 [Promise - JavaScript | MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise) [Promise.prototype.then() - JavaScript | MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) [異步函數 - 提高 Promise 的易用性 | Web | Google Developers](https://developers.google.com/web/fundamentals/primers/async-functions?hl=zh-tw)