ES6 Promise 进击(二)

作者 johansen 日期 2017-04-13
ES6 Promise 进击(二)

这周我们结合JQ,对比前面学习过的Promise,进行分析。

Promise API

上周我们谈到了关于Promise的两个API,then以及catch。今天介绍resolve,reject,all。


1. Promise.resolve

一般情况下我们都会使用 new Promise() 来创建promise对象,但是除此之外我们也可以使用其他方法。在这里,我们将会学习如何使用 Promise.resolvePromise.reject这两个方法。

静态方法Promise.resolve(value) 可以认为是 new Promise() 方法的快捷方式。
比如 Promise.resolve(42); 可以认为是以下代码的语法糖。

1
2
3
4
5
6
7
8
9
//方法1
new Promise(function(resolve){
resolve(42);
});
//方法2
//两个方法的表述方式不同,但实质是相通的。
Promise.resolve(42).then(function(value){
console.log(value);
});

强调一个 promise.resolve(thenable);接收的thenable对象具体指什么(类Promise对象:拥有名为.then方法的对象),我们举个例子。
到底什么样的对象能算是thenable的呢,最简单的例子就是 jQuery.ajax(),它的返回值就是thenable的。
因为jQuery.ajax() 的返回值是 jqXHR Object 对象,这个对象具有 .then 方法。
$.ajax('/json/comment.json');//拥有 .then方法的对象
这个thenable的对象可以使用 Promise.resolve 来转换为一个promise对象。
变成了promise对象的话,就能直接使用 then 或者 catch 等这些在 ES6 Promises里定义的方法了。

将thenable对象转换promise对象

1
2
3
4
var promise = Promise.resolve($.ajax('/json/comment.json'));//promise对象
promise.then(function(value){
console.log(value);
});

2. Promise.reject

Promise.reject(error)是和 Promise.resolve(value) 类似的静态方法,是 new Promise() 方法的快捷方式。
比如 Promise.reject(new Error(“出错了”)) 就是下面代码的语法糖形式。

1
2
3
new Promise(function(resolve,reject){
reject(new Error("出错了"));
});

这段代码的功能是调用该promise对象通过then指定的 onRejected 函数,并将错误(Error)对象传递给这个 onRejected 函数。

1
2
3
Promise.reject(new Error("BOOM!")).catch(function(error){
console.error(error);
});

3. Promise.all

Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。
如下批量XHR的请求结果的例子,使用 Promise.all 的话代码会非常简单。

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
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
var request = {
comment: function getComment() {
return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
},
people: function getPeople() {
return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
}
};
function main() {
function recordValue(results, value) {
results.push(value);
return results;
}
// [] 用来保存初始化的值
var pushValue = recordValue.bind(null, []);
return request.comment().then(pushValue).then(request.people).then(pushValue);
}
// 运行的例子
main().then(function (value) {
console.log(value);
}).catch(function(error){
console.error(error);
});

之前例子中的 getURL 返回了一个promise对象,它封装了XHR通信的实现。 向 Promise.all 传递一个由封装了XHR通信的promise对象数组的话,则只有在全部的XHR通信完成之后(变为FulFilled或Rejected状态)之后,才会调用 .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
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
var request = {
comment: function getComment() {
return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
},
people: function getPeople() {
return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
}
};
function main() {
return Promise.all([request.comment(), request.people()]);
}
// 运行示例
main().then(function (value) {
console.log(value);
}).catch(function(error){
console.log(error);
});

这个例子的执行方法和前面的例子一样。 不过Promise.all 在以下几点和之前的例子有所不同。

  • main中的处理流程显得非常清晰。
  • Promise.all 接收 promise对象组成的数组作为参数。Promise.all([request.comment(), request.people()]);

在上面的代码中,request.comment() 和 request.people() 会同时开始执行,而且每个promise的结果(resolve或reject时传递的参数值),和传递给 Promise.all 的promise数组的顺序是一致的。
也就是说,这时候 .then 得到的promise数组的执行结果的顺序是固定的,即 [comment, people]。

1
2
3
main().then(function (results) {
console.log(results); // 按照[comment, people]的顺序
});

4. Promise.race

和 Promise.all 类似的对多个promise对象进行处理的 Promise.race 方法。
Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理, 与之相对的是 Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var winnerPromise = new Promise(function (resolve) {
setTimeout(function () {
console.log('this is winner');
resolve('this is winner');
}, 4);
});
var loserPromise = new Promise(function (resolve) {
setTimeout(function () {
console.log('this is loser');
resolve('this is loser');
}, 1000);
});
// 第一个promise变为resolve后程序停止
Promise.race([winnerPromise, loserPromise]).then(function (value) {
console.log(value); // => 'this is winner'
});

上面例子我们还发现,Promise.race 在第一个promise对象变为Fulfilled之后,并不会取消其他promise对象的执行。