## Promise
Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最早提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了`Promise`對象。
所謂`Promise`,簡單說就是一個容器,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理。
有了`Promise`對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,`Promise`對象提供統一的接口,使得控制異步操作更加容易。
### 簡單示例
```
function p (num) {
return new Promise((resolve, reject) => {
if (num >= 0) {
resolve(num)
} else {
reject(new Error('參數不能小于 0'))
}
})
}
```
這里,返回了一個 Promise 對象,如果成功將執行 resolve(num),使用的時候用 then 接收,如果失敗,執行 reject() 并拋出一個錯誤。
成功的調用
```
console.log('-1-')
p(10).then(num => {
console.log(num)
}).catch(e => {
console.log(e)
})
console.log('-2-')
```
輸出
```
-1-
-2-
10
```
失敗的調用
```
console.log('-1-')
p(-1).then(num => {
console.log(num)
}).catch(e => {
console.log(e)
})
console.log('-2-')
```
輸出
```
-1-
-2-
Error: 參數不能小于 0
```
### Promise 新建后就會立即執行
```
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
```
輸出
```
Promise
Hi!
resolved
```
上面代碼中,Promise 新建后立即執行,所以首先輸出的是Promise。然后,then方法指定的回調函數,將在當前腳本所有同步任務執行完才會執行,所以resolved最后輸出。
### 可以在 .then 回調里返回 Promise
可以在 then 回調里返回 Promise!返回的 promise 會在接下來的 then 被自動打開(unwrapped)
示例,函數仍然使用上述 p() 函數
```
console.log('-1-')
p(10).then(num => {
console.log(num)
return p(15)
}).then(num => {
console.log(num)
return p(-5)
}).catch(e => {
console.log(e)
})
console.log('-2-')
```
輸出
```
-1-
-2-
10
15
Error: 參數不能小于 0
```
### 每次調用 .then 都會產生一個新的 Promise
在鏈式調用 Promise 時,可以不顯示聲明 return 語句,then 默認會返回一個 Promise
示例,函數仍然使用上述 p() 函數
```
p(10).then(num => (num >= 10 ? 'ok' : 'no'))
.then(console.log)
p(1).then(num => (num >= 10 ? 'ok' : 'no'))
.then(console.log)
```
輸出
```
ok
no
```
注意,每次調用 then 返回的都是不同的 Promise 實例,即 p() 、'ok'、'no' 返回的是三個不同的 Promise 實例,如果 p 出錯,則會被 rejected,但 p().then 中返回的都是 resolve。
### 將 callback API 轉換為 Promise
在 Node.js 中,推薦使用 util.promisify。用來將回調 API 轉成 promise 式的:
```
const { promisify } = require('util');
const fs = require('fs');
const readFileAsync = promisify(fs.readFile);
readFileAsync('myfile.txt', 'utf-8')
.then( r => console.log(r) )
.catch( e => console.error(e));
```
### Promise.prototype.finally()
finally 方法用于指定不管 Promise 對象最后狀態如何,都會執行的操作。該方法是 ES2018 引入標準的。
```
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···})
```
不管promise最后的狀態,在執行完then或catch指定的回調函數以后,都會執行finally方法指定的回調函數。