鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - Promise、Async/Await 深入解析

深入讲解 JavaScript 中 Promise、Async/Await 的运作原理、使用方式、常见方法及实现,帮助你轻松应对面试中的相关问题。

影片縮圖

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

Promise 基本概念

Promise 是 JavaScript 中处理异步操作的标准方式,代表一个尚未完成但未来会完成的操作。Promise 有三种状态:

  • Pending(等待中):初始状态,既不是成功也不是失败
  • Fulfilled(已实现):操作成功完成
  • Rejected(已拒绝):操作失败
const promise = new Promise((resolve, reject) => {
  // Asynchronous operation
  if (/* success */) {
    resolve(value);  // state changes to fulfilled
  } else {
    reject(error);   // state changes to rejected
  }
});

为何需要 Promise?

Promise 解决了传统回调函数的多个问题,提供了更优雅的异步处理方式:

  • 避免回调地狱:解决嵌套回调导致的代码难以维护问题
  • 统一错误处理:使用 catch 方法统一处理错误
  • 链式调用:通过 then 方法实现清晰的流程控制
  • 更好的语义化:Promise 的状态转换更符合程序设计直觉
// 传统回调方式
doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log(finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

// Promise 方式
doSomething()
  .then(result => doSomethingElse(result))
  .then(newResult => doThirdThing(newResult))
  .then(finalResult => console.log(finalResult))
  .catch(failureCallback);

Async/Await 的优势

Async/Await 是建立在 Promise 之上的语法糖,提供了更直观的方式来处理异步操作:

  • 同步风格:使异步代码看起来像同步代码
  • 错误处理:可以使用传统的 try/catch 处理错误
  • 调试方便:可以像同步代码一样逐行调试
  • 条件处理:更容易实现复杂的条件判断逻辑
async function fetchUserData() {
  try {
    const response = await fetch('/api/user');
    const userData = await response.json();
    return userData;
  } catch (error) {
    console.error('Error fetching user data:', error);
    throw error;
  }
}

Promise 的静态方法

Promise.all()

并行执行多个 Promise,等待全部完成。如果任一 Promise 失败,整个操作就失败。

Promise.all([
  fetch('/api/users'),
  fetch('/api/posts')
]).then(([users, posts]) => {
  // process results
});

Promise.race()

返回最先完成的 Promise 结果,无论成功或失败。

Promise.race([
  fetch('/api/data'),
  new Promise((_, reject) =>
    setTimeout(() => reject('Timeout'), 5000)
  )
]);

Promise.any()

返回第一个成功的 Promise 结果,只有全部失败时才会拒绝。

Promise.any([
  fetch('https://api1.example.com'),
  fetch('https://api2.example.com')
]).then(firstSuccess => {
  // Handle the first successful result
});

Promise.allSettled()

等待所有 Promise 完成,返回每个 Promise 的结果状态。

Promise.allSettled([
  fetch('/api/users'),
  fetch('/api/posts')
]).then(results => {
  // process all results
});

🔥 常见面试题目

(一)Promise 的状态转换规则是什么?

解答:Promise 的状态转换有以下特点:

  • 初始状态为 Pending(等待中)
  • 只能从 Pending 转换到 Fulfilled(已实现)或 Rejected(已拒绝)
  • 状态转换是不可逆的,一旦改变就不能再变化
  • 状态转换时会触发对应的回调函数(then 或 catch)

(二)Promise.all 和 Promise.race 的区别?

解答:

特性Promise.allPromise.race
完成条件所有 Promise 都完成任一 Promise 完成
失败处理任一失败则整体失败采用最先完成的结果
返回值所有结果的数组最先完成的结果

(三)async/await 和 Promise 的关系?

解答:

  • async/await 是建立在 Promise 之上的语法糖
  • async 函数总是返回一个 Promise
  • await 只能在 async 函数内使用
  • await 可以等待任何 "thenable" 对象(实现了 then 方法的对象)
// async 函数总是返回 Promise
async function example() {
  return "Hello";
}

// 等同于
function example() {
  return Promise.resolve("Hello");
}

// 验证 async 返回的是 Promise
console.log(example()); // Promise {<fulfilled>: "Hello"}

// await 解开的是 Promise resolve 的值
async function example() {
  const result = await example(); // 等待 Promise.resolve("Hello") 的结果
  console.log(result); // "Hello"
  
  // 等同于
  example().then(value => console.log(value)); // "Hello"
}

// 完整范例比较
// Promise 方式
function fetchData() {
  return fetch('/api/data')
    .then(response => response.json())
    .then(data => data)
    .catch(error => console.error(error));
}

// async/await 方式 - 更易读的同步风格
async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data; // 自动被包装为 Promise.resolve(data)
  } catch (error) {
    console.error(error);
  }
}

(四)如何实现自己的 Promise.all?

解答:我们可以实现一个简单的 MyPromise.all 函数,模拟 Promise.all 的功能:

function myPromiseAll(promises) {
  return new Promise((resolve, reject) => {
    // 如果传入的不是数组,直接拒绝
    if (!Array.isArray(promises)) {
      return reject(new TypeError('promises must be an array'));
    }
    
    const results = [];  // 存储所有 promise 的结果
    let completed = 0;   // 已完成的 promise 数量
    
    // 如果是空数组,直接返回空数组结果
    if (promises.length === 0) {
      return resolve(results);
    }
    
    // 遍历所有 promise
    promises.forEach((promise, index) => {
      // 使用 Promise.resolve 确保处理非 Promise 值
      Promise.resolve(promise)
        .then(result => {
          results[index] = result;  // 保持原始顺序
          completed++;
          
          // 当所有 promise 都完成时,解析最终结果
          if (completed === promises.length) {
            resolve(results);
          }
        })
        .catch(error => {
          // 任何一个 promise 拒绝,整个 myPromiseAll 就拒绝
          reject(error);
        });
    });
  });
}

// 使用范例
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 100));
const p3 = Promise.resolve(3);

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

这个实现的关键点:

  • 返回一个新的 Promise
  • 追踪所有 Promise 的完成状态
  • 保持结果的原始顺序
  • 任一 Promise 失败时立即拒绝整个 Promise
  • 使用 Promise.resolve 处理非 Promise 值