鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - 手写 Promise.all 实现

学习如何手写实现 JavaScript Promise.all 方法,掌握处理多个异步任务的技巧,提升前端面试竞争力。

影片縮圖

懒得看文章?那就来看视频吧

Promise.all 方法概述

Promise.all 是 JavaScript 中处理多个 Promise 的重要方法,它接收一个 Promise 数组(或可迭代对象),并返回一个新的 Promise,该 Promise 在所有输入的 Promise 都成功解析时才会成功,或者在任一 Promise 被拒绝时立即拒绝。

Promise.all 的特性

  • 1. 并行执行:所有 Promise 同时开始执行
  • 2. 等待全部:等待所有 Promise 都完成
  • 3. 一个失败则全失败:任一 Promise 拒绝,整体立即拒绝
  • 4. 保持结果顺序:结果数组顺序与输入数组顺序一致
  • 5. 非 Promise 值:自动使用 Promise.resolve() 包装

手写实现 Promise.all 是前端面试中的热门题目,它可以检验对 Promise、异步处理和数组操作的理解。

原生 Promise.all 使用方式

在实现之前,先来看看 Promise.all 的原生使用方式:

// 创建三个 Promise
const promise1 = Promise.resolve(1);
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const promise3 = Promise.resolve(3);

// 使用 Promise.all 并行处理三个 Promise
Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log(results); // [1, 2, 3] (顺序与输入数组相同)
  })
  .catch(error => {
    console.error('至少有一个 Promise 被拒绝', error);
  });

// 处理拒绝的例子
const promise4 = Promise.resolve(4);
const promise5 = Promise.reject('错误发生');
const promise6 = Promise.resolve(6);

Promise.all([promise4, promise5, promise6])
  .then(results => {
    console.log('不会执行到这里');
  })
  .catch(error => {
    console.error(error); // '错误发生' (来自被拒绝的 promise5)
  });

手写实现 Promise.all

现在让我们来实现 Promise.all 方法:

// 基本实现 Promise.all
function promiseAll(promises) {
  // 检查输入是否为可迭代对象
  if (!promises[Symbol.iterator]) {
    return Promise.reject(new TypeError('参数必须是可迭代的'));
  }
  
  // 将输入转换为数组
  const promiseArray = Array.from(promises);
  const len = promiseArray.length;
  
  // 创建一个新的 Promise
  return new Promise((resolve, reject) => {
    // 空数组直接返回空结果
    if (len === 0) {
      return resolve([]);
    }
    
    // 存储结果的数组
    const results = new Array(len);
    // 完成的 Promise 计数
    let resolvedCount = 0;
    
    // 处理每个 Promise
    promiseArray.forEach((promise, index) => {
      // 将非 Promise 值包装为 Promise
      Promise.resolve(promise)
        .then(value => {
          // 按原顺序存储结果
          results[index] = value;
          resolvedCount++;
          
          // 当所有 Promise 都完成时,解析整体 Promise
          if (resolvedCount === len) {
            resolve(results);
          }
        })
        .catch(error => {
          // 任一 Promise 被拒绝,整体 Promise 立即拒绝
          reject(error);
        });
    });
  });
}

让我们测试一下这个实现:

// 测试案例
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 100));
const p3 = 3; // 非 Promise 值,将被自动包装

promiseAll([p1, p2, p3])
  .then(results => {
    console.log(results); // [1, 2, 3]
  })
  .catch(error => {
    console.error(error);
  });

// 测试错误案例
const p4 = Promise.resolve(4);
const p5 = Promise.reject('失败');
const p6 = Promise.resolve(6);

promiseAll([p4, p5, p6])
  .then(results => {
    console.log('不会执行到这里');
  })
  .catch(error => {
    console.error(error); // '失败'
  });