PJCHENder 未整理筆記

[JS] Promise 的使用

2017-09-26

[JS] Promise 的使用

@(Javascript)[ES6, JavaScript]

1
2
3
4
5
6
7
8
9
const p = new Promise(callback<resolve, reject>)

p.then(<resolveHandler>, [<rejectHandler>])
.then(<resolveHandler2>, <rejectHandler2>)
.catch(<rejectHandler>)
.finally(<finallyHandler>)

Promise.all('<array>')
Promise.race('<array>')

使用範例:

範例:

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
const p = new Promise ((resolve, reject) => {
setTimeout(function(){resolve(3)}, 1000)
})
/**
* 方法一:在 callback 中 return new Promise
**/
p.then((value) => {
return new Promise ((resolve, reject) => {
console.log(value) // 3
setTimeout(function(){resolve(value * 2)}, 1000)
})
})

/**
* 方法二:直接代入會 return Promise 的 function
**/
p.then(promiseFn) // 6
.then((value) => {
console.log(value) // 12
})
.catch(err => { // catch any error in "then"
console.log(err)
})

function promiseFn (value) {
console.log(value) // 6
return new Promise((resolve, reject) => {
setTimeout(function() {resolve(value * 2)}, 1000)
})
}

目錄

[TOC]

觀念

  • Promise 有三種狀態:pending, resolved/fulfilled, rejected
  • new Promise 內的函式會立即被執行,當 resolve 得到內容後,才會執行 .then
  1. .thenresolvedCallback 中,可以得到在 new Promise 中 resolve 內所得到的值(value)。
  2. 如果在 .thenresolvedCallbackreturn 一個值,則這個值會以 Promise 物件的形式傳到下一個 .then
  3. 因此在下一個 .thenresolvedCallback 中,可以取得上一個 .then 中 return 的值。
  4. 但如果我們在 .then 中是 return 另一個 new Promise ,則下一個 .then 會等到這個 Promise 中的 resolve 得到值後才執行。
  5. 且在下一個 .thenresolvedCallback 中,可以得到上一個 new Promiseresolve 的值

基本使用

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 基本使用
* 在 new Promise 內的函式會被馬上執行,
* 當 resolve 得到內容後,才會執行 .then。
**/

const myPromise = new Promise((resolve, reject) => {
console.log('In new Promise, start callback') // 立即執行
setTimeout(() => {
// 一秒後執行
let response = 10
resolve(response)
}, 1000)
})

myPromise.then((value) => {
// 在 myPromise 被 resolve 時執行
console.log('The answer is ' + value)
})

myPromise.catch((error) => {
// 在 myPromise 被 reject 時執行
console.log('error', error)
})

Basic Promise @ JSFiddle

Promise 使用 .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
/**
* Promise 使用 .then 串接
* 在 .then 裡面 resole(value) 的 value 一樣是 promise 物件,
* 可以被傳到下一個 .then 中使用。
**/

const myPromise = new Promise((resolve, reject) => {
console.log('In new Promise, start callback') // 立即執行
setTimeout(() => {
let data = 10
resolve(data) // 1 秒後執行
}, 1000)
})

// 在 .then 裡面在 return resolve 的 value
// 這個新的 value 會被傳到下一個 promise 的 resolver 內
myPromise.then((value) => {
console.log('first .then') // 被 resolve 後執行
return value + 3
})
.then((value) => {
// 得到上一個 .then return 的值後執行
console.log('second .then')
console.log('The final value is ' + value)
})

我們可以透過 .then() 去 return 另一個 Promise,主要方法有兩種:

  1. 在 Promise 的 .then callback 中去 new 另一個 Promise
  2. 直接在 .then 中透過 function 的方式 new Promise 而非在 callback 中執行

如果是在 Promise 的 .then callback 中去 new 另一個 Promise,要把這個 Promise 前面加上 return;如果是直接把它放在 .then 的 function 中,則不用在前面加上 return。

方法一:在 .then 的 callback 中 new 另一個 Promise

我們可以在一個 Promise 的 .then 中去 return 另一個 new Promise,:

1
2
3
4
5
6
7
8
9
.then((value) => {
return new Promise((resolve, reject) => {
resolve(value + 3)
})
})
// 這個 .then 會等到上面那個 Promise 被 resolved 後才執行
.then((value)=>{
console.log(value)
})

範例一

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
40
41
42
43
44
45
/**
* 在 Promise 中 new 另一個 Promise
**/

const myPromise = new Promise((resolve, reject) => {
// 立即執行
console.log('In first Promise, start callback')
setTimeout(() => {
let data = 10
resolve(data) // 2 秒後執行
console.log('In fist Promise, resolve data')
}, 1500)
})

// 當前面 Promise 的 resolve 得到內容後,才會執行 .then
myPromise.then((value) => {
// 在.then 的 resolvedCallback 中,可以得到在 new Promise 中 resolve 內所得到的值(value)。
console.log('In first then, the value is ' + value)
// 如果在 .then 的 resolvedCallback 中 return 一個值,則這個值會以 Promise 物件的形式傳到下一個 .then
return value + 3
})

.then((value) => {
// 因此在這個 .then 的 resolvedCallback 中,可以取得上一個 .then 中 return 的值。
console.log('In second then, the value is ' + value)
console.log('In second Promise, start callback')

// 如果我們在 .then 中是 return 另一個 new Promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// 2 秒後再執行
resolve(value + 3)
console.log('In second Promise, resolve data')
}, 1500)
})
})

// 則下一個 .then 會等到這個 Promise 中的 resolve 得到值後才執行。
.then((value) => {
// 在這個 .then 的 resolvedCallback 中,可以得到上一個 new Promise 中 resolve 的值
console.log('In third then, the value is ' + value)
})
.catch((reason) => {
console.log('Request Failed ' + reason)
})

output 的結果會分成兩個時間點:

1
2
3
4
5
6
7
8
9
10
--------- 立即執行
"In first Promise, start callback"
--------- 1.5 秒後執行
"In fist Promise, resolve data"
"In first then, the value is 10"
"In second then, the value is 13"
"In second Promise, start callback"
--------- 再 1.5 秒後執行
"In second Promise, resolve data"
"In third then, the value is 16"

範例二

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
/**
* EXAMPLE2: Return a new Promise in a Promise
**/

function waitASecond(seconds) {
console.log('start', seconds)
return new Promise((resolve, reject) => {
setTimeout(function() {
seconds++
resolve(seconds)
}, 1000)
})
}

waitASecond(0)
.then((seconds) => {
console.log('In first .then', seconds)
return waitASecond(seconds)
})
.then((seconds) => {
console.log('In second .then', seconds)
return waitASecond(seconds)
})
.then((seconds) => {
console.log('In third .then', seconds)
})

Promise Chain: return a Promise in a Promise @ JSFiddle

方法二:在 .then 中代入會 return Promise 的函式

範例一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// EXAMPLE 1
function waitASecond(second) {
console.log(second)
return new Promise((resolve, reject) => {
setTimeout(function() {
second++
resolve(second)
}, 1000)
})
}

waitASecond(0)
.then(waitASecond)
.then(waitASecond)
.then((second) => {
console.log(second)
})

範例二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// EXAMPLE2
const calculatePromise = new Promise((resolve, reject) => {
console.log('In new Promise, start callback')
setTimeout(() => {
let answer = 3 + 5
resolve(answer)
}, 1000)
})

function addTwo (value) {
console.log('current value', value)
return value + 2
}

function printResult (value) {
console.log('The final value is ' + value)
}

calculatePromise.then(addTwo)
.then(addTwo)
.then(printResult)

Use new Promise directly in .then without putting in callback @ JSFiddle

Promise 錯誤處理

可以在 Promise 的任何一個階段中加上 .catch(<err>) 做為錯誤處理,除非有特殊需要,不然 .catch() 通常會放在最後,在任何一個 .then() 的階段中有錯誤發生時,就會直接跳到最後的 .catch() 而不會繼續執行錯誤發生後的 .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
36
37
38
39
40
/**
* Error Handling in Promise
**/

function waitASeconds(seconds){
return new Promise((resolve, reject) => {
if (seconds > 3){
reject('seconds is bigger then 3')
}
setTimeout(function(){
seconds++
resolve(seconds)
},1000)
})
}

waitASeconds(0)
.then((seconds => {
console.log('In first .then', seconds)
return waitASeconds(seconds)
}))
.then((seconds => {
console.log('In second .then', seconds)
return waitASeconds(seconds)
}))
.then((seconds => {
console.log('In third .then', seconds)
return waitASeconds(seconds)
}))
.then((seconds => {
console.log('In forth .then', seconds)
return waitASeconds(seconds)
}))
.then((seconds => {
console.log('In fifth .then', seconds)
return waitASeconds(seconds)
}))
.catch((error) => {
console.warn(error)
})

Error Handling in Promise @ JSFiddle

進階使用

keywords: Promise.all(_Array_), Promise.race(_Array_)

這個建構式另外還提供兩種調用 promise 的方法 race() 與 all():
Promise.all(): 此方法可以同時執行大量 Promise 物件,並且在 “全部” 完成後回傳陣列。
Promise.race(): 此方法執行大量 Promise 物件,但僅會回傳最快回應的結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)
});

此部分來自 JavaScript ES6 Promise @ 前端,沒有極限 by 卡斯伯

Demo how to use Promise.all() 和 Promise.race() @ JSFiddle

實際例子

利用 Promise 先透過 email 和 password 取得 access_key 和 secret 後,再用 access_key 和 secret 取得 token。

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
40
/**
* You can get token in a Promise
**/

const getTokenPromise = new Promise ((resolve, reject) => {
request.post(endpoint + '/users/cert')
.send({
email: '<your_email>',
password: '<your_password>'
})
.end((err, res) => {
resolve(res)
reject(err)
})
})

getTokenPromise
.then((response) => {
let parseResponse = JSON.parse(response.text)
console.log(parseResponse)
return new Promise((resolve, reject) => {
request.post(endpoint + '/users/token')
.send({
access_key: parseResponse.access_key,
secret: parseResponse.secret
})
.end((err, res) => {
resolve(res)
reject(err)
})
})
})
.then((response) => {
let parseResponse = JSON.parse(response.text)
// You can get Token Here
console.log(parseResponse)
})
.catch((err) => {
console.warn('getTokenPromise with error', err)
})

參考

掃描二維條碼,分享此文章