面试官:Promise是什么?跟await async有什么区别?异常怎么捕获?

155 阅读5分钟

问题:Promise?跟 await async 有什么区别?异常怎么捕获?

解答:
Promise 和 async/await 都是 JavaScript 中用于处理异步操作的机制,它们的目标都是让异步代码更加简洁和易于理解。虽然它们可以实现类似的功能,但在语法、使用方式和异常处理上有一些重要的区别。下面我们详细解释 Promise 和 async/await 的区别,并介绍如何在这两种情况下捕获异常。

1. 什么是 Promise?

定义:

Promise 是一个表示异步操作最终完成(或失败)的对象。它有三种状态:

  • pending(进行中):初始状态,既没有被解决也没有被拒绝。
  • fulfilled(已解决):操作成功完成,Promise 返回一个值。
  • rejected(已拒绝):操作失败,Promise 返回一个错误对象。

使用方式:

Promise 通常用于处理异步操作的结果。你可以使用 .then() 处理成功的回调,使用 .catch() 处理失败的回调。

实例:使用 Promise
// 创建一个 Promise
const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = { message: '数据获取成功' };
      if (Math.random() > 0.5) {
        resolve(data); // 操作成功
      } else {
        reject(new Error('数据获取失败')); // 操作失败
      }
    }, 1000);
  });
};

// 使用 .then() 和 .catch() 处理 Promise
fetchData()
  .then(data => {
    console.log('成功:', data.message);
  })
  .catch(error => {
    console.error('失败:', error.message);
  });

异常捕获:

  • .then() :用于处理 Promise 成功的情况。
  • .catch() :用于捕获 Promise 被拒绝时的错误。
  • 链式调用:你可以通过链式调用多个 .then() 和 .catch() 来处理多个异步操作。
注意:
  • 如果在 .then() 中抛出错误,Promise 会自动进入 rejected 状态,并触发后续的 .catch()
  • 如果你没有提供 .catch(),未处理的 Promise 拒绝会被视为未捕获的错误,可能会导致控制台警告或应用崩溃。

2. 什么是 async/await

定义:

async/await 是基于 Promise 的语法糖,它使得异步代码看起来像同步代码,从而提高了代码的可读性和维护性。async 关键字用于声明一个函数为异步函数,await 关键字用于等待一个 Promise 的结果。

使用方式:

  • async:将函数标记为异步函数,返回一个隐式的 Promise
  • await:暂停函数的执行,直到 Promise 被解决或拒绝。await 只能在 async 函数内部使用。
实例:使用 async/await
// 定义一个异步函数
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error('请求失败');
    }
    const data = await response.json();
    console.log('成功:', data);
  } catch (error) {
    console.error('失败:', error.message);
  }
}

fetchData();

异常捕获:

  • try...catchasync/await 与 try...catch 结合使用来捕获异步操作中的错误。await 表达式如果遇到 Promise 拒绝,会抛出错误,进入 catch 块。
  • 简化错误处理:相比 Promise 的链式 .then() 和 .catch()async/await 的 try...catch 更加直观,减少了嵌套层级,代码更易读。

3. Promise 和 async/await 的区别

特性Promiseasync/await
语法使用 .then() 和 .catch()使用 async 和 await
链式调用支持链式调用不需要链式调用,代码更简洁
异常处理使用 .catch()使用 try...catch
返回值显式返回 Promise隐式返回 Promise
错误传播未处理的 Promise 拒绝会导致未捕获错误未处理的错误会抛出并可以被捕获
可读性代码可能较为冗长,容易出现回调地狱代码更简洁,易于阅读

4. 异常捕获的详细说明

4.1. Promise 中的异常捕获

  • .catch() :用于捕获 Promise 被拒绝时的错误。
  • 链式调用中的错误传播:如果在一个 .then() 中抛出错误,Promise 会自动进入 rejected 状态,并触发后续的 .catch()
  • 全局错误捕获:如果你没有提供 .catch(),未处理的 Promise 拒绝会被视为未捕获的错误,可能会导致控制台警告或应用崩溃。你可以使用 window.onunhandledrejection 来捕获这些未处理的 Promise 拒绝。
实例:Promise 中的异常捕获
fetchData()
  .then(data => {
    console.log('成功:', data.message);
    // 故意抛出错误
    throw new Error('手动抛出错误');
  })
  .catch(error => {
    console.error('失败:', error.message);
  });

// 全局捕获未处理的 Promise 拒绝
window.addEventListener('unhandledrejection', event => {
  console.error('捕获到未处理的 Promise 拒绝:', event.reason);
});

4.2. async/await 中的异常捕获

  • try...catchasync/await 与 try...catch 结合使用来捕获异步操作中的错误。await 表达式如果遇到 Promise 拒绝,会抛出错误,进入 catch 块。
  • 简化错误处理:相比 Promise 的链式 .then() 和 .catch()async/await 的 try...catch 更加直观,减少了嵌套层级,代码更易读。
实例:async/await 中的异常捕获
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error('请求失败');
    }
    const data = await response.json();
    console.log('成功:', data);
  } catch (error) {
    console.error('失败:', error.message);
  }
}

fetchData();

4.3. 并发异步操作中的异常捕获

  • Promise.all() :用于并发执行多个 Promise,并等待所有 Promise 完成。如果其中一个 Promise 被拒绝,Promise.all() 会立即返回 rejected 状态。
  • Promise.allSettled() :用于并发执行多个 Promise,并等待所有 Promise 完成,无论成功还是失败。它不会因为某个 Promise 拒绝而提前终止,而是返回每个 Promise 的结果。
实例:并发异步操作中的异常捕获

// 使用 Promise.all() 并发执行多个 Promise
const promise1 = Promise.resolve('成功1');
const promise2 = Promise.reject('失败2');
const promise3 = Promise.resolve('成功3');

Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log('所有 Promise 成功:', results);
  })
  .catch(error => {
    console.error('其中一个 Promise 失败:', error);
  });

// 使用 Promise.allSettled() 并发执行多个 Promise
Promise.allSettled([promise1, promise2, promise3])
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Promise ${index + 1} 成功:`, result.value);
      } else {
        console.error(`Promise ${index + 1} 失败:`, result.reason);
      }
    });
  });

5. 总结

  • Promise 是处理异步操作的基础,提供了链式调用和 .then().catch() 方法来处理成功和失败的情况。它的优点是灵活性高,适合复杂的异步操作链。
  • async/await 是基于 Promise 的语法糖,使得异步代码看起来像同步代码,代码更加简洁和易读。它与 try...catch 结合使用,能够更好地处理异常。
  • 异常捕获:无论是 Promise 还是 async/await,都可以通过 .catch() 或 try...catch 来捕获异步操作中的错误。对于未处理的 Promise 拒绝,建议使用 window.onunhandledrejection 进行全局捕获,以防止应用崩溃。

进一步探讨:

  • 你是否已经在项目中使用过 Promise 或 async/await?你觉得它们各自的优缺点是什么?
  • 你是否有遇到过复杂的异步操作链?你是如何处理这些操作的依赖关系和错误的?
  • 你是否想了解更多关于并发异步操作的最佳实践?例如如何使用 Promise.all()Promise.race() 等方法来优化异步任务的执行。