到目前为止我们已经学习了如何通过 [`.then`](http://liubin.github.io/promises-book/#promise.then) 和 [`.catch`](http://liubin.github.io/promises-book/#promise.catch) 来注册回调函数,这些回调函数会在promise对象变为 FulFilled 或 Rejected 状态之后被调用。
如果只有一个promise对象的话我们可以像前面介绍的那样编写代码就可以了,如果要在多个promise对象都变为FulFilled状态的时候才要进行某种处理话该如何操作呢?
我们以当所有XHR(异步处理)全部结束后要进行某操作为例来进行说明。
各位读者现在也许有点难以在大脑中描绘出这么一种场景,我们可以先看一下下面使用了普通的回调函数风格的XHR处理代码。
## 2.7.1\. 通过回调方式来进行多个异步调用
multiple-xhr-callback.js
~~~
function getURLCallback(URL, callback) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
callback(null, req.responseText);
} else {
callback(new Error(req.statusText), req.response);
}
};
req.onerror = function () {
callback(new Error(req.statusText));
};
req.send();
}
// <1> 对JSON数据进行安全的解析
function jsonParse(callback, error, value) {
if (error) {
callback(error, value);
} else {
try {
var result = JSON.parse(value);
callback(null, result);
} catch (e) {
callback(e, value);
}
}
}
// <2> 发送XHR请求
var request = {
comment: function getComment(callback) {
return getURLCallback('http://azu.github.io/promises-book/json/comment.json', jsonParse.bind(null, callback));
},
people: function getPeople(callback) {
return getURLCallback('http://azu.github.io/promises-book/json/people.json', jsonParse.bind(null, callback));
}
};
// <3> 启动多个XHR请求,当所有请求返回时调用callback
function allRequest(requests, callback, results) {
if (requests.length === 0) {
return callback(null, results);
}
var req = requests.shift();
req(function (error, value) {
if (error) {
callback(error, value);
} else {
results.push(value);
allRequest(requests, callback, results);
}
});
}
function main(callback) {
allRequest([request.comment, request.people], callback, []);
}
// 运行的例子
main(function(error, results){
if(error){
return console.error(error);
}
console.log(results);
});
~~~
这段回调函数风格的代码有以下几个要点。
* 直接使用 `JSON.parse` 函数的话可能会抛出异常,所以这里使用了一个包装函数 `jsonParse`
* 如果将多个XHR处理进行嵌套调用的话层次会比较深,所以使用了 `allRequest` 函数并在其中对request进行调用。
* 回调函数采用了 `callback(error,value)` 这种写法,第一个参数表示错误信息,第二个参数为返回值
在使用 `jsonParse` 函数的时候我们使用了 `bind` 进行绑定,通过使用这种偏函数(Partial Function)的方式就可以减少匿名函数的使用。(如果在函数回调风格的代码能很好的做到函数分离的话,也能减少匿名函数的数量)
~~~
jsonParse.bind(null, callback);
// 可以认为这种写法能转换为以下的写法
function bindJSONParse(error, value){
jsonParse(callback, error, value);
}
~~~
在这段回调风格的代码中,我们也能发现如下一些问题。
* 需要显示进行异常处理
* 为了不让嵌套层次太深,需要一个对request进行处理的函数
* 到处都是回调函数
下面我们再来看看如何使用 `Promise#then` 来完成同样的工作。
## 2.7.2\. 使用Promise#then同时处理多个异步请求
需要事先说明的是 `Promise.all` 比较适合这种应用场景的需求,因此我们故意采用了大量 `.then` 的晦涩的写法。
使用了[`.then`](http://liubin.github.io/promises-book/#promise.then) 的话,也并不是说能和回调风格完全一致,大概重写后代码如下所示。
multiple-xhr.js
~~~
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);
});
~~~
将上述代码和[回调函数风格](http://liubin.github.io/promises-book/#multiple-xhr-callback.js)相比,我们可以得到如下结论。
* 可以直接使用 `JSON.parse` 函数
* 函数 `main()` 返回promise对象
* 错误处理的地方直接对返回的promise对象进行处理
向前面我们说的那样,main的 `then` 部分有点晦涩难懂。
为了应对这种需要对多个异步调用进行统一处理的场景,Promise准备了 `Promise.all` 和 `Promise.race` 这两个静态方法。
在下面的小节中我们将对这两个函数进行说明。
- 前言
- 第一章 - 什么是Promise
- 1.1. 什么是Promise
- 1.2. Promise简介
- 1.3. 编写Promise代码
- 第二章 - 实战Promise
- 2.1. Promise.resolve
- 2.2. Promise.reject
- 2.3. 专栏: Promise只能进行异步操作?
- 2.4. Promise#then
- 2.5. Promise#catch
- 2.6. 专栏: 每次调用then都会返回一个新创建的promise对象
- 2.7. Promise和数组
- 2.8. Promise.all
- 2.9. Promise.race
- 2.10. then or catch?
- 第三章 - Promise测试
- 3.1. 基本测试
- 3.2. Mocha对Promise的支持
- 3.3. 编写可控测试(controllable tests)
- 第四章 - Advanced
- 4.1. Promise的实现类库(Library)
- 4.2. Promise.resolve和Thenable
- 4.3. 使用reject而不是throw
- 4.4. Deferred和Promise
- 4.5. 使用Promise.race和delay取消XHR请求
- 4.6. 什么是 Promise.prototype.done ?
- 4.7. Promise和方法链(method chain)
- 4.8. 使用Promise进行顺序(sequence)处理
- 第五章 - Promises API Reference
- 5.1. Promise#then
- 5.2. Promise#catch
- 5.3. Promise.resolve
- 5.4. Promise.reject
- 5.5. Promise.all
- 5.6. Promise.race
- 第六章 - 用語集
- 第七章 - 参考网站
- 第八章 - 关于作者
- 第九章 - 关于译者