面试高频考点——手写promise.all

890 阅读3分钟

手写 Promise.MyAll 的实现

手写Promise.all是面试中的一道常考题,我们可以尝试手写一个类似的函数 Promise.MyAll。以下是实现代码及其详细解释。

56665.jpg


Promise.all()功能特点

在 JavaScript 中,Promise.all 是一个非常有用的方法,它允许我们同时处理多个 Promise 对象,并在所有 Promise 都成功完成时返回一个包含所有结果的数组。如果其中任何一个 Promise 失败,Promise.all 会立即返回失败的原因。

代码实现

// 在面试中注释的书写十分重要

/**
 * 手写 Promise.MyAll 方法
 * @param {Promise[]} promises - 一个包含多个 Promise 对象的数组
 * @return {Promise} - 返回一个新的 Promise 对象
 * @desc
 * 1. 如果所有 Promise 都成功,返回一个包含所有结果的数组,结果的顺序与输入顺序一致。
 * 2. 如果有一个 Promise 失败,立即返回失败的原因。
 */
Promise.MyAll = (promises) => {
  // 用于存储每个 Promise 的结果
  let arr = [],
      count = 0; // 用于记录已完成的 Promise 数量

  return new Promise((resolve, reject) => {
    promises.forEach((item, i) => {
      item
        .then(res => {
          arr[i] = res; // 将结果按顺序存入数组
          count++;
          // 如果所有 Promise 都已完成,则 resolve 结果数组
          if (count === promises.length) {
            resolve(arr);
          }
        })
       .catch(err => {
          reject(err)
        })
    });
  });
};

代码解析

  1. 参数与返回值

    • promises:一个包含多个 Promise 对象的数组。
    • 返回值:一个新的 Promise 对象。如果所有 Promise 都成功,则 resolve 一个包含所有结果的数组;如果有一个 Promise 失败,则 reject 失败的原因。
  2. 核心逻辑

    • 使用一个数组 arr 来存储每个 Promise 的结果。
    • 使用计数器 count 来记录已经完成的 Promise 数量。
    • 遍历 promises 数组,对每个 Promise 调用 then 方法:
      • 如果 Promise 成功,将结果按顺序存入 arr 数组,并检查是否所有 Promise 都已完成。如果是,则调用 resolve(arr)
      • 如果 Promise 失败,catch err。
  3. 注意事项

    • 结果的顺序与输入 Promise 的顺序一致,这是通过将结果按索引存入数组实现的。
    • 如果有一个 Promise 失败,Promise.MyAll 会立即返回失败的原因,而不会等待其他 Promise 完成。

代码优化

我们可以使用到 .then 的第二个参数,也是一个函数,当 Promise 被拒绝(rejected)时调用,该函数接收拒绝的原因(通常是一个错误对象)作为其参数

Promise.MyAll = (promises) => {
  // promises resolved 后的结果
  let arr = [],
      count = 0;
  return new Promise((resolve, reject) => {
    promises.forEach((item, i) => {
      // 微任务
      // then 两个参数 第一个是成功的回调 第二个是失败的回调
      item
        .then(res => {
          arr[i] = res;  // 按照顺序保存结果
          count++;
          if (count === promises.length) {
            resolve(arr)
          }
        }, reject)  // .then 的第二个参数 失败直接调用reject
        // .cathch(err => {
        //   reject(err)
        // })
    })
  })
}

示例测试

以下是一个使用 Promise.MyAll 的示例:

const weather = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve({ temp: 29, conditions: 'Sunny with Clouds' });
    // reject('error'); // 可以测试失败的情况
  }, 3000);
});

const tweets = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('I like cake, BBQ is ready now!');
    // reject('BBQ 糊了'); // 可以测试失败的情况
  }, 5000);
});

Promise
  .MyAll([weather, tweets])
  .then(res => {
    console.log(res); // 输出所有 Promise 的结果
  })
  .catch(err => {
    console.log(err); // 输出失败的原因
  });

运行结果

  1. 所有 Promise 成功的情况

    • 输出:
      [
        { temp: 29, conditions: 'Sunny with Clouds' },
        'I like cake, BBQ is ready now!'
      ]
      
  2. 有一个 Promise 失败的情况

    • 如果 tweetsreject,输出:
      BBQ 糊了
      

总结

通过手写 Promise.MyAll,我们可以更深入地理解 Promise.all 的工作原理。其核心逻辑是通过遍历 Promise 数组,使用 then 方法监听每个 Promise 的状态,并在所有 Promise 都成功时返回结果数组,或者在某个 Promise 失败时立即返回失败原因。

这种实现方式不仅帮助我们理解了 Promise 的运行机制,还展示了如何通过计数器、数组索引等技巧来保证结果的顺序和正确性。