极客时间返利平台,你可以在上边通过山月的链接购买课程,并添加我的微信 (shanyue94) 领取返现。
山月训练营之面试直通车 服务上线了,从准备简历、八股文准备、项目经历准备、面试、面经、面经解答、主观问题答复、谈薪再到入职的一条龙服务。

# 如何实现一个 Promise.all

更多描述

await Promise.all([1, 2, 3]);
//-> [1, 2, 3]

await Promise.all([1, Promise.resolve(2), 3]);
//-> [1, 2, 3]

await Promise.all([1, Promise.resolve(2)]);
//-> [1, 2]

await Promise.all([1, Promise.reject(2)]);
//-> Throw Error: 2

Issue

欢迎在 Gtihub Issue 中回答此问题: Issue 500 (opens new window)

代码可在线调试: 如何实现一个 Promise.all - codepen (opens new window)

有一次头条面试,一道手写题目是:如何手写实现 promise.all

我从来没有想过要手写实现 promise.all 函数,稍微一想,大概就是维护一个数组,把所有 promise 给 resolve 了之后都扔进去,这有啥子好问的。没想到,一上手还稍微有点棘手。

先来看一个示例吧:

await Promise.all([1, Promise.resolve(2)]);
//-> [1, 2]

await Promise.all([1, Promise.reject(2)]);
//-> Throw Error: 2
  1. 传入一个 Iterable,但大部分情况下是数组,以下以数组代替
  2. 传入一个数组,其中可包含 Promise,也可包含普通数据
  3. 数组中 Prmise 并行执行
  4. 但凡有一个 Promise 被 Reject 掉,Promise.all 失败
  5. 保持输出数组位置与输入数组一致
  6. 所有数据 resolve 之后,返回结果
function pAll(_promises) {
  return new Promise((resolve, reject) => {
    // Iterable => Array
    const promises = Array.from(_promises);
    // 结果用一个数组维护
    const r = [];
    const len = promises.length;
    let count = 0;
    for (let i = 0; i < len; i++) {
      // Promise.resolve 确保把所有数据都转化为 Promise
      Promise.resolve(promises[i])
        .then((o) => {
          // 因为 promise 是异步的,保持数组一一对应
          r[i] = o;

          // 如果数组中所有 promise 都完成,则返回结果数组
          if (++count === len) {
            resolve(r);
          }
          // 当发生异常时,直接 reject
        })
        .catch((e) => reject(e));
    }
  });
}

为了测试,实现一个 sleep 函数

const sleep = (seconds) =>
  new Promise((resolve) => setTimeout(() => resolve(seconds), seconds));

以下示例进行测试,没有问题

pAll([1, 2, 3]).then((o) => console.log(o));
pAll([sleep(3000), sleep(2000), sleep(1000)]).then((o) => console.log(o));
pAll([sleep(3000), sleep(2000), sleep(1000), Promise.reject(10000)])
  .then((o) => console.log(o))
  .catch((e) => console.log(e, "<- Error"));
Promise.all = function (promises) {
  const len = promises.length;
  const result = new Array(len);
  let countDone = 0;
  return new Promise((resolve, reject) => {
    if (len === 0) {
      resolve(result);
    }
    for (let i = 0; i < len; i++) {
      const promise = promises[i];
      Promise.resolve(promise).then(
        (data) => {
          result[i] = data;
          countDone++;
          if (countDone === len) {
            resolve(result);
          }
        },
        (error) => {
          reject(error);
        }
      );
    }
  });
};

Author

回答者: Vi-jay (opens new window)

function all(arr) {
  return new Promise((resolve, reject) => {
    const results = [];
    arr.forEach(async (val) => {
      Promise.resolve(val)
        .then((data) => {
          results.push(data);
          if (results.length === arr.length) return resolve(results);
        })
        .catch((e) => reject(e));
    });
  });
}

Author

回答者: voezy (opens new window)

function all(arr) {
  return new Promise((resolve, reject) => {
    const results = [];
    arr.forEach(async (val) => {
      Promise.resolve(val)
        .then((data) => {
          results.push(data);
          if (results.length === arr.length) return resolve(results);
        })
        .catch((e) => reject(e));
    });
  });
}

@Vi-jay 这个写法里,每个 promise 结束时间可能不一样,直接用 push 而不是数组循环索引去记录的话感觉可能会有问题。

Last Updated: 9/27/2022, 2:39:59 PM