# 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)