JavaScript ES6 Promise

Promise 一直都是 JavaScript 夢寐以求的功能,非同步的處理如果沒有使用 Promise 經常導致函式或資料無法正常的運作。本篇避開許多深度的詞彙,直接透過範例來瞭解怎麼運行。

簡單的 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
// 宣告 promise 建構式
let newPromise = new Promise((resolve, reject) => {
// 傳入 resolve 與 reject,表示資料成功與失敗
let ran = parseInt(Math.random() * 2); // 隨機成功或失敗
console.log('Promise 開始')
if (ran) {
setTimeout(function(){
// 3 秒時間後,透過 resolve 來表示完成
resolve('3 秒時間(fulfilled)');
}, 3000);
} else {
// 回傳失敗
reject('失敗中的失敗(rejected)')
}
});
newPromise.then((data)=> {
// 成功訊息 (需要 3 秒)
console.log(data);
}).catch((err)=> {
// 失敗訊息 (立即)
console.log(err)
});

以上就是一個基本的 Promise 範例,執行 Promise 建構式時還會再帶入 resolve 與 reject 的 callback function。

  • resolve: 完成的 callback
  • reject: 失敗的 callback

Promise 的生命週期

  • pending: 等待中的初始狀態
  • fulfilled: 正確完成
  • rejected: 已拒絕,操作失敗

上圖就是一個轉化中的圖 (一開始是 Pending,等我展開的時候已經 resolved 了… )。

這個建構式另外還提供兩種調用 promise 的方法 race() 與 all():

  • Promise.all(): 此方法可以同時執行大量 Promise 物件,並且在 “全部” 完成後回傳陣列。
  • Promise.race(): 此方法執行大量 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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 宣告 promise 建構式
let newPromise1 = new Promise((resolve, reject) => {
let ran = parseInt(Math.random() * 5000); // 隨機成功或失敗
setTimeout(function(){
resolve('隨機時間完成');
}, ran);
});
let newPromise2 = new Promise((resolve, reject) => {
setTimeout(function(){
resolve('2 秒完成');
}, 2000);
});
let newPromise3 = new Promise((resolve, reject) => {
setTimeout(function(){
resolve('3 秒完成');
}, 3000);
});
let newPromise4 = new Promise((resolve, reject) => {
reject('失敗');
});
Promise.all([newPromise1, newPromise2, newPromise3, newPromise4]).then((data)=> {
// 一次性同時回傳成功訊息,回傳以上三個數值的陣列
console.log(data);
}).catch( err => {
// 失敗訊息 (立即)
console.log(err)
});
Promise.race([newPromise1, newPromise2, newPromise3]).then((data)=> {
// 僅會回傳一個最快完成的 resolve 或 reject
console.log('race', data);
}).catch( err => {
// 失敗訊息 (立即)
console.log(err)
});

這個案例較長,前面主要是宣告幾個 Promise 建構式,差異在時間與刻意的失敗;後面介紹 Promise.all() 與 Promise.race() 的差異。

Chain 鏈接方法

Promise 一個很重要的方法 then(),其實是可以不斷的做鏈接。基本的概念就是前一個 return 會是下一個 then() 傳送的變數。

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
26
27
28
29
30
31
32
33
34
35
// 前面宣告的不重要
let newPromise1 = new Promise((resolve, reject) => {
let ran = parseInt(Math.random() * 5000); // 隨機成功或失敗
setTimeout(function(){
resolve('隨機時間完成');
}, ran);
});
let newPromise2 = new Promise((resolve, reject) => {
setTimeout(function(){
resolve('2 秒完成');
}, 2000);
});
let newPromise3 = new Promise((resolve, reject) => {
setTimeout(function(){
resolve('3 秒完成');
}, 3000);
});
// 這段以前不重要
// 鏈接方法
newPromise1.then((data1) => {
console.log('data1', data1);
return newPromise2.then((data2) => {
return `${data2} + ${data1}` // 回傳 Promise 內的值,讓下一個 then 可以接收
});
}).then((data3) => {
console.log('data3', data3);
return newPromise3.then((data4) => {
return `${data4} + ${data3}` // 回傳 Promise 內的值,讓下一個 then 可以接收
});
}).then((data5) => {
console.log(`最後的 + ${data5}`)
});

用文字不好表達這段,直接看圖。Promise 執行時可以使用 then 做串接,串接的方法在於需要使用 return 來做下一個 then() 的傳入值,透過這個方法可以避免 JavaScript 原始碼過巢導致難以閱讀。

以上程式碼皆可直接複製貼到 Chrome 開發者工具內的 console 運行。