2.8. Promise.all

Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。

前面我们看到的批量获得若干XHR的请求结果的例子,使用 Promise.all 的话代码会非常简单。

之前例子中的 getURL 返回了一个promise对象,它封装了XHR通信的实现。 向 Promise.all 传递一个由封装了XHR通信的promise对象数组的话,则只有在全部的XHR通信完成之后(变为FulFilled或Rejected状态)之后,才会调用 .then 方法。

promise-all-xhr.js

  1. function getURL(URL) {
  2. return new Promise(function (resolve, reject) {
  3. var req = new XMLHttpRequest();
  4. req.open('GET', URL, true);
  5. req.onload = function () {
  6. if (req.status === 200) {
  7. resolve(req.responseText);
  8. } else {
  9. reject(new Error(req.statusText));
  10. }
  11. };
  12. req.onerror = function () {
  13. reject(new Error(req.statusText));
  14. };
  15. req.send();
  16. });
  17. }
  18. var request = {
  19. comment: function getComment() {
  20. return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
  21. },
  22. people: function getPeople() {
  23. return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
  24. }
  25. };
  26. function main() {
  27. return Promise.all([request.comment(), request.people()]);
  28. }
  29. // 运行示例
  30. main().then(function (value) {
  31. console.log(value);
  32. }).catch(function(error){
  33. console.log(error);
  34. });

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

  • main中的处理流程显得非常清晰

  • Promise.all 接收 promise对象组成的数组作为参数

  1. Promise.all([request.comment(), request.people()]);

在上面的代码中,request.comment()request.people() 会同时开始执行,而且每个promise的结果(resolve或reject时传递的参数值),和传递给 Promise.all 的promise数组的顺序是一致的。

也就是说,这时候 .then 得到的promise数组的执行结果的顺序是固定的,即 [comment, people]。

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

如果像下面那样使用一个计时器来计算一下程序执行时间的话,那么就可以非常清楚的知道传递给 Promise.all 的promise数组是同时开始执行的。

promise-all-timer.js

  1. // `delay`毫秒后执行resolve
  2. function timerPromisefy(delay) {
  3. return new Promise(function (resolve) {
  4. setTimeout(function () {
  5. resolve(delay);
  6. }, delay);
  7. });
  8. }
  9. var startDate = Date.now();
  10. // 所有promise变为resolve后程序退出
  11. Promise.all([
  12. timerPromisefy(1),
  13. timerPromisefy(32),
  14. timerPromisefy(64),
  15. timerPromisefy(128)
  16. ]).then(function (values) {
  17. console.log(Date.now() - startDate + 'ms');
  18. // 約128ms
  19. console.log(values); // [1,32,64,128]
  20. });

timerPromisefy 会每隔一定时间(通过参数指定)之后,返回一个promise对象,状态为FulFilled,其状态值为传给 timerPromisefy 的参数。

而传给 Promise.all 的则是由上述promise组成的数组。

  1. var promises = [
  2. timerPromisefy(1),
  3. timerPromisefy(32),
  4. timerPromisefy(64),
  5. timerPromisefy(128)
  6. ];

这时候,每隔1, 32, 64, 128 ms都会有一个promise发生 resolve 行为。

也就是说,这个promise对象数组中所有promise都变为resolve状态的话,至少需要128ms。实际我们计算一下Promise.all 的执行时间的话,它确实是消耗了128ms的时间。

从上述结果可以看出,传递给 Promise.all 的promise并不是一个个的顺序执行的,而是同时开始、并行执行的。

如果这些promise全部串行处理的话,那么需要 等待1ms → 等待32ms → 等待64ms → 等待128ms ,全部执行完毕需要225ms的时间。

要想了解更多关于如何使用Promise进行串行处理的内容,可以参考第4章的Promise中的串行处理中的介绍。