Promise.all和连续运行Promises之间的区别?

时间:2017-08-19 01:23:06

标签: javascript

说我有许多承诺。

const promises = [ /*...*/ ];

当所有的承诺都得到解决时,我希望有所作为。以下两种方法之间的区别是什么:

  1. 使用Promise.all

    const allDonePromise = Promise.all(promises);
    
  2. 按顺序运行承诺

    async function allPromises(promisesArray) {
      const results = [];
    
      for (const promise of promisesArray) {
        results.push(await promise);
      }
    
      return results;
    }
    
    const allDonePromises = allPromises(promises);
    
  3. Promise.all只是一个内置函数来执行allPromises所做的事情,或者是否有其他事情发生在幕后以使Promise.all更快。我在哪里可以找到有关Promise.all的内部信息的信息?

1 个答案:

答案 0 :(得分:2)

您可以在section 25.4.4.1 of the ECMAScript 2015 Language Specification中找到Promise.all的规格。

您自己的实施确实做得对。差异在于细节:

以上规范在第25.4.4.1.1.r点说明要在每个承诺上调用then。这些调用是同步发生的(NB:不是它们的回调)。每当任何一个promise解析时, remainingElementsCount 就会递减(参见步骤2.10)。每当它变为零时,Promise.all返回的承诺就会被解决(NB:同步!)。

现在想象你有一个百万个承诺的数组,第一个需要花费最长时间才能解决,然后你的函数仍然需要在函数返回之前执行999999等待,而规范中的算法已经处理了分辨率那些999999承诺之前第一个解决了,并且在第一个承诺最终解决之后没什么可做的。

你可以在this polyfill/promise.js implementation中看到这一点(通过递增计数发生):

shaka.polyfill.Promise.all = function(others) {
  var p = new shaka.polyfill.Promise();
  if (!others.length) {
    p.resolve_([]);
    return p;
  }
  // The array of results must be in the same order as the array of Promises
  // passed to all().  So we pre-allocate the array and keep a count of how
  // many have resolved.  Only when all have resolved is the returned Promise
  // itself resolved.
  var count = 0;
  var values = new Array(others.length);
  var resolve = function(p, i, newValue) {
    shaka.asserts.assert(p.state_ != shaka.polyfill.Promise.State.RESOLVED);
    // If one of the Promises in the array was rejected, this Promise was
    // rejected and new values are ignored.  In such a case, the values array
    // and its contents continue to be alive in memory until all of the Promises
    // in the array have completed.
    if (p.state_ == shaka.polyfill.Promise.State.PENDING) {
      values[i] = newValue;
      count++;
      if (count == values.length) {
        p.resolve_(values);
      }
    }
  };
  var reject = p.reject_.bind(p);
  for (var i = 0; i < others.length; ++i) {
    if (others[i].then) {
      others[i].then(resolve.bind(null, p, i), reject);
    } else {
      resolve(p, i, others[i]);
    }
  }
  return p;
};

但请注意,浏览器实施方式不同。上面的polyfill只是可能的实现之一。

请注意,您的功能不是&#34;按顺序运行承诺&#34; 。承诺是运行无论你是否对它们做了什么:它们在你构建它们之后立即完成它们的工作。

唯一被序列化的东西是你开始关注(即等待)各自的承诺决议的时刻。规范似乎暗示实现应该从一开始就听取所有承诺的回调。你无法在循环中使用await实现(好吧,你可以,但是你需要重复调​​用async函数,每个承诺一次,这不会给你带来任何好处await超过then,因为您需要对then函数返回的承诺应用async

然后在this和参数验证的区域中存在一些其他(明显的)差异。值得注意的是ECMA规范声明:

  

all函数要求其this值是一个构造函数,它支持Promise构造函数的参数约定。