前端知库

怎样实现保持输入顺序的 Promise.all 教学版本?

参考答案

核心是把每个结果写回输入索引,用计数判断是否全部完成。普通值与 thenable 通过 Promise.resolve 统一处理;某项拒绝时整体拒绝,但不会取消其他已启动操作。

下面是本项目编写的教学版本,返回原生 Promise,支持常规可迭代输入;不模拟原生方法对子类构造器、被改写的内建方法等全部规范细节。生产代码优先使用原生 Promise.all。

JavaScript
function promiseAll(iterable) {
  return new Promise((resolve, reject) => {
    const results = [];
    let remaining = 1; // 保留一个计数,避免遍历结束前提前完成
    let index = 0;
    for (const item of iterable) {
      const position = index++;
      remaining++;
      Promise.resolve(item).then(value => {
        results[position] = value;
        if (--remaining === 0) resolve(results);
      }, reject);
    }
    if (--remaining === 0) resolve(results);
  });
}

空输入得到空数组;完成顺序不同也不改变结果顺序。不可迭代输入或迭代过程抛错,会通过 Promise 构造器转成拒绝。这里的聚合不是并发数限制器:传入已经执行的异步任务,无法让它们回到尚未启动的状态。