返回首页

13. 说说你对Promise的理解?Promise.all和Promise.race的区别?

问题解析

Promise是JavaScript异步编程的重要解决方案,理解Promise的原理、状态变化以及静态方法的使用是前端面试中的高频考点。本题主要考察对Promise核心概念的理解以及Promise.all和Promise.race的区别和应用场景。

核心概念

什么是Promise

Promise是ES6引入的异步编程解决方案,它是一个对象,代表了一个异步操作的最终完成(或失败)及其结果值。Promise有三种状态:

  • pending(进行中):初始状态,既未完成也未拒绝
  • fulfilled(已成功):操作成功完成
  • rejected(已失败):操作失败

Promise的特点

  1. 状态一旦改变,就不会再变:Promise对象的状态改变只有两种可能:从pending变为fulfilled,或者从pending变为rejected
  2. 链式调用:通过then方法可以进行链式调用,解决回调地狱问题
  3. 错误捕获:通过catch方法可以统一捕获错误

详细解答

Promise基本用法

const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    if (/* 异步操作成功 */) {
      resolve(value); // 将Promise状态变为fulfilled
    } else {
      reject(error); // 将Promise状态变为rejected
    }
  }, 1000);
});

promise
  .then(value => {
    // 成功时的回调
    console.log(value);
  })
  .catch(error => {
    // 失败时的回调
    console.error(error);
  })
  .finally(() => {
    // 无论成功失败都会执行
    console.log('完成');
  });

Promise.all

Promise.all用于将多个Promise实例包装成一个新的Promise实例。

const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);

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

特点

  • 所有Promise都成功时,返回的Promise才会成功,结果是一个数组,按传入顺序排列
  • 只要有一个Promise失败,整个Promise.all就失败,返回第一个失败的Promise的原因

Promise.race

Promise.race同样是将多个Promise实例包装成一个新的Promise实例,但只要有一个实例率先改变状态,整个Promise的状态就跟着改变。

const p1 = new Promise((resolve) => {
  setTimeout(resolve, 500, 'one');
});
const p2 = new Promise((resolve) => {
  setTimeout(resolve, 1000, 'two');
});

Promise.race([p1, p2])
  .then(value => {
    console.log(value); // 'one',因为p1先完成
  });

特点

  • 哪个Promise先完成(无论成功或失败),就采用哪个Promise的结果
  • 常用于设置超时处理

Promise.all与Promise.race的区别

特性 Promise.all Promise.race
触发条件 所有Promise完成 第一个Promise完成
成功结果 所有Promise结果的数组 第一个完成的Promise结果
失败条件 任意一个Promise失败 第一个完成的Promise失败
应用场景 并行执行多个异步操作 竞速、超时控制

深入理解

Promise的链式调用

fetch('/api/user')
  .then(response => response.json())
  .then(user => fetch(`/api/posts/${user.id}`))
  .then(response => response.json())
  .then(posts => console.log(posts))
  .catch(error => console.error(error));

Promise.all的实际应用

// 并行加载多个资源
async function loadResources() {
  try {
    const [users, posts, comments] = await Promise.all([
      fetch('/api/users').then(r => r.json()),
      fetch('/api/posts').then(r => r.json()),
      fetch('/api/comments').then(r => r.json())
    ]);
    return { users, posts, comments };
  } catch (error) {
    console.error('加载资源失败:', error);
  }
}

Promise.race的超时控制

function fetchWithTimeout(url, timeout = 5000) {
  return Promise.race([
    fetch(url),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('请求超时')), timeout)
    )
  ]);
}

最佳实践

  1. 始终处理错误:使用catch捕获Promise链中的错误
  2. 避免Promise嵌套:使用链式调用代替嵌套
  3. 合理使用Promise.all:并行执行独立的异步操作,提高效率
  4. 注意错误处理:Promise.all中一个失败会导致全部失败,如需单独处理可使用Promise.allSettled
// 使用Promise.allSettled获取所有结果,无论成功失败
Promise.allSettled([promise1, promise2, promise3])
  .then(results => {
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        console.log('成功:', result.value);
      } else {
        console.log('失败:', result.reason);
      }
    });
  });

面试要点

  1. 能够清晰解释Promise的三种状态及其转换
  2. 理解Promise如何解决回调地狱问题
  3. 掌握Promise.all和Promise.race的区别和使用场景
  4. 了解async/await与Promise的关系(async函数返回Promise)
  5. 能够手写简单的Promise实现