Promise 筆記

Promise 筆記

十二月 08, 2017

介紹

Promises/A+標準定義

所謂的Promises/A+標準,其實就是個幾千字的一頁網頁而已,裡面的說明與用語並不會太難理解。雖然ES6標準中也有自己的Promise物件標準章節,但因為裡面涉及很多實作技術說明,明顯地用字遣詞艱澀許多,所以在這裡就不多加討論。以下使用Promises/A+標準作為一個開始,來解說Promise的標準裡有什麼內容。

專門用語

  • promise (承諾)是一個帶有遵照這個規格的then方法的物件
  • thenable 是一個有定義then方法的物件
  • value 合法的JavaScript值(包含undefined、thenable與promise)
  • exception (例外)使用throw語句丟出來的值
  • reason (理由)是表明為什麼promise被拒絕(rejected)的值

註: 另外有個常見的專有名詞 settled(固定的) 一個promise最後的狀態,也就是fulfilled(已實現)或rejected(已拒絕)
註: reason(理由)通常是一個Error物件,用於錯誤處理。
註: promise/帕咪死/ 的中文翻譯是”承諾”、”約定”,本書中並不會用它的中文翻譯字詞,都是直接用英文。

Promise狀態

promise物件必定是以下三種狀態中的其中一種: pending(等待中)、fulfilled(已實現)或rejected(已拒絕)。

2.1.1 當處在pending(等待中)時,一個promise:

2.1.1.1 可能會轉變到不是fulfilled(已實現)就是rejected(已拒絕)狀態

2.2.1 當處在fulfilled(已實現)時,一個promise:

2.2.1.1 必定不會再轉變到其他任何狀態

2.2.1.2 必定有不能再更動的值

2.3.1 當處在rejected(已拒絕)時,一個promise:

2.3.1.1 必定不會再轉變到其他任何狀態

2.3.1.2 必定有不能再更動的值reason(理由)

這個用下面的圖解說明,應該可以很清楚的理解:
Imgur

狀態是在Promise結構很重要的一個屬性,因為promise物件一開始都是代表懸而未決的值,所以一開始在promise物件在建立時,狀態都是pending(等待中),之後可以轉變到fulfilled(已實現)就是rejected(已拒絕)其中一個,然後就固定不變了。如果有產生value(值)的情況就是轉變到fulfilled(已實現)狀態,而如果是有reason(理由)時,代表要轉變到rejected(已拒絕)狀態。

建立一個 Promise

看完上面的比喻讓我們對應到 Javascript。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var isMomHappy = false
var willIGetNewPhone = new Promise(function (resolve, reject) {
if (isMomHappy) {
var phone = {
brand: 'Samsung',
color: 'black',
type: 's8'
}
resolve(phone)
} else {
var reason = new Error('Mom is unhappy')
reject(reason)
}
})

第一行我們使用一個 Boolean isMomHappy 定義媽媽是否開心。
我們宣告一個 Promise _willIGetNewPhone_。這個 Promise 可能是被 履行(resolved) 又或者 _拒絕(rejected)_。
Promise 標準的語法可以參考 MDN

new Promise(function (resolve, reject) {})

我們需要記得的是如果一個 Promise 執行成功要在內部 function 呼叫 resolve(成功結果) ,如果結果是失敗則呼叫 reject(失敗結果) 。在我們的範例中如果媽媽開心,我們將得到手機因此我們執行 _reslove(phone)_,如果媽媽不高興則執行 _reject(reason)_。

使用 Promise

到這一步我們已經有了一個 Promise,讓我們接著來使用它。

1
2
3
4
5
6
7
8
9
10
var askMom = function () {
willIGetNewPhone
.then(function (fulfilled) {
console.log(fulfilled)
})
.catch(function (error) {
console.log(error.message)
})
}
askMom()
  1. 首先我們有個 function 叫 askMom 在這個 function 中,我們將利用 Promise willIGetNewPhone。
  2. 我們希望一旦等待的結果發生時可以採取對應的動作,我們可以使用 .then 或 .catch 來執行對應的行為。
  3. 在這個範例中,我們在 .then 中使用 function (fulfilled){},而這個 fulfilled 就是從 Promise 的 resolve(成功結果) 傳來的結果,範例中這個結果就是 phone 物件。
  4. 在 .catch 中我們使用了 function (error) {}。而這個 error 就是從 Promise 的 reject(失敗結果) 傳來的即 reason。

鏈式調用 Promise

假如你承諾您的朋友,如果你拿到新手機會借他們看看。這又是另一個 Promise。讓我們繼續來撰寫這個範例:

1
2
3
4
5
6
var showOff = function (phone) {
return new Promise(function (resolve, reject) {
var message = 'Hey friend, I have a new ' + phone.color + ' ' + phone.brand + ' phone' + phone.type
resolve(message)
})
}
  1. 在這個範例,您可能發現到我們根本沒有呼叫 reject,這是可選的,我們可以省略不調用。
  2. 另外,我們可以透過使用 Promise.resolve 簡化這個範例。
1
2
3
4
var showOff = function (phone) {
var message = 'Hey friend, I have a new ' + phone.color + ' ' + phone.brand + ' phone ' + phone.type
return Promise.resolve(message)
}

接著讓我們來看看如何串連 Promise。在 willIGetNewPhone 這個 Promise 之後接續 showOff Promise。

1
2
3
4
5
6
7
8
9
10
11
var askMom = function () {
willIGetNewPhone
.then(showOff)
.then(function (fulfilled) {
console.log(fulfilled)
// 'Hey friend, I have a new black Samsung phone s8'
})
.catch(function (error) {
console.log(error.message)
})
}

這就是 Promise 串連的方式。

非同步

Promise 是非同步的,讓我們在呼叫 Promise 的前後加上 console.log

1
2
3
4
5
6
7
8
9
10
11
12
13
var askMom = function () {
console.log('before asking Mom')
willIGetNewPhone
.then(showOff)
.then(function (fulfilled) {
console.log(fulfilled)
})
.catch(function (error) {
console.log(error.message)
})

console.log('after asking Mom')
}

順序是

1
2
3
1. before asking Mom
2. after asking Mom
3. Hey friend, I have a new black Samsung phone s8

ES5, ES6/ES2015,ES7/Next 的 Promise

完整範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const isMomHappy = true
const willIGetNewPhone = new Promise((resolve, reject) => {
if (isMomHappy) {
const phone = {
brand: 'Samsung',
color: 'black',
type: 's8'
}
resolve(phone)
} else {
const reason = new Error('Mom is not happy)
reject(reason)
}
})
const showOff = function (phone) {
const message = 'Hey friend, I have a new ' + phone.color + ' ' + phone.brand + ' phone ' + phone.type
return Promise.resolve(message)
}
const askMom = function () {
willIGetNewPhone
.then(showOff)
.then(fulfilled => console.log(fulfilled))
.catch(error => console.log(error.message))
}
askMom()