优雅而高效的JavaScript——Promise 和 async/await

发布时间:2023年12月18日

在这里插入图片描述

🙂博主:小猫娃来啦
🙂文章核心:优雅而高效的JavaScript——Promise 和 async/await

引言

在现代的web开发中,异步操作已经成为一种常见情况。在处理异步操作时,我们需要一种有效的方法来管理和处理这些操作,以确保代码的可读性和可维护性。Promise是一种用于处理异步操作的编程模式,而async/await则是对Promise的一种语法糖。本文将详细介绍Promise和async/await的相关概念以及它们的优势。


Promise介绍

Promise的基本概念

Promise是一种用于处理异步操作的编程模式。它将异步操作封装在一个对象中,并提供了一些方法来处理异步操作的结果。Promise对象有三种状态:pending、fulfilled和rejected。在异步操作完成后,Promise对象的状态会从pending变为fulfilled或rejected。如果操作成功完成,Promise对象的状态将为fulfilled,并且可以获取异步操作的结果。如果操作失败,Promise对象的状态将为rejected,并且可以获取失败的原因。

Promise的三种状态

  • Pending(进行中):初始状态,表示异步操作正在进行中。
  • Fulfilled(已完成):表示异步操作成功完成,并可以获取到操作的结果。
  • Rejected(已失败):表示异步操作失败,并可以获取到失败的原因。

使用Promise处理异步操作的例子

下面是一个使用Promise处理异步操作的例子:

function fetchData() {
  return new Promise((resolve, reject) => {
    // 模拟异步操作
    setTimeout(() => {
      const data = 'Hello, world!';
      resolve(data);
    }, 2000);
  });
}

fetchData()
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error(error);
  });

在上面的例子中,fetchData函数返回一个Promise对象。在Promise的构造函数中,我们执行了一个异步操作,即在2秒后解析结果并将其传递给resolve函数。然后我们使用then方法来处理异步操作的结果。如果操作成功完成,then方法中的回调函数将被调用,并且可以获取到异步操作的结果。如果操作失败,catch方法中的回调函数将被调用,并且可以获取到失败的原因。


Promise的问题

回调地狱

当我们需要处理多个异步操作,并且这些操作之间存在依赖关系时,使用Promise链式调用将会产生回调地狱的问题。这是由于Promise链式调用的嵌套和回调函数带来的额外层级。回调地狱使得代码难以阅读、理解和维护。

Promise链式调用带来的复杂性

虽然Promise可以优雅地处理异步操作,但在复杂的情况下,用Promise链式调用来组织和管理多个异步操作会变得比较复杂。每个then方法中的回调函数可能会涉及到各种逻辑和处理,使得代码的可读性和可维护性下降。


promise的方法

当使用 Promise 进行异步编程时,Promise 提供了一些方法来处理多个异步任务的结果。

  1. Promise.all 方法:
    Promise.all 方法接收一个由 Promise 对象组成的数组,并返回一个新的 Promise 对象。该新的 Promise 对象在数组中所有的 Promise 对象都变为 fulfilled 状态时才会变为 fulfilled 状态,且返回值是一个包含所有 Promise 对象结果的数组。

示例:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Hello'), 2000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('World'), 1000);
});

Promise.all([promise1, promise2])
  .then(result => {
    console.log(result); // 输出:['Hello', 'World']
  })
  .catch(error => {
    console.error(error);
  });

在上述示例中,Promise.all 方法将 promise1promise2 两个 Promise 对象组合起来,并等待这两个 Promise 对象都变为 fulfilled 状态后返回结果。

  1. Promise.reject 方法:
    Promise.reject 方法返回一个状态为 rejected 的 Promise 对象,可以用于直接拒绝一个 Promise。

示例:

Promise.reject(new Error('Something went wrong'))
  .catch(error => {
    console.error(error); // 输出:Error: Something went wrong
  });

在上述示例中,通过 Promise.reject 方法创建了一个被拒绝的 Promise 对象,并通过 catch 方法捕获到了拒绝的原因。

  1. Promise.resolve 方法:
    Promise.resolve 方法返回一个状态为 fulfilled 的 Promise 对象,可以用于直接解析一个值或已经被解析的 Promise 对象。

示例:

Promise.resolve(42)
  .then(result => {
    console.log(result); // 输出:42
  });

const resolvedPromise = Promise.resolve('Resolved');
Promise.resolve(resolvedPromise)
  .then(result => {
    console.log(result); // 输出:Resolved
  });

在上述示例中,Promise.resolve 方法分别创建了一个已解析的 Promise 对象和一个直接解析的 Promise 对象,并通过 then 方法获取到了解析的结果。

  1. Promise.race 方法:
    Promise.race 方法接收一个由 Promise 对象组成的数组,并返回一个新的 Promise 对象。该新的 Promise 对象在数组中任意一个 Promise 对象变为 fulfilled 或 rejected 状态时就会变为相应的状态,并返回第一个变为 fulfilled 或 rejected 状态的 Promise 对象的结果。

示例:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Hello'), 2000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('Error')), 1000);
});

Promise.race([promise1, promise2])
  .then(result => {
    console.log(result); // 输出:'Error'
  })
  .catch(error => {
    console.error(error); // 输出:Error: Error
  });

在上述示例中,Promise.race 方法将 promise1promise2 两个 Promise 对象组合起来,并返回第一个完成(无论是 fulfilled 还是 rejected)的结果。

通过使用 Promise.allPromise.rejectPromise.resolvePromise.race 方法,我们可以更好地处理异步任务的结果,提高代码的可读性和可维护性。


async/await的引入

为了解决Promise带来的复杂性和回调地狱的问题,JavaScript引入了async/await语法。async/await是对Promise的一种语法糖,它使异步代码更加简洁和易读。

async/await的概念

  • async:通过将async关键字添加到函数前面来定义一个异步函数。异步函数内部可以使用await关键字暂停函数的执行,并等待Promise对象解析。
  • await:await关键字用于等待一个Promise对象的解析。在等待解析期间,函数的执行将暂停,直到异步操作成功完成并返回结果。

使用async/await处理异步操作的例子

下面是一个使用async/await处理异步操作的例子:

function fetchData() {
  return new Promise((resolve, reject) => {
    // 模拟异步操作
    setTimeout(() => {
      const data = 'Hello, world!';
      resolve(data);
    }, 2000);
  });
}

async function getData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

getData();

在上面的例子中,getData函数被定义为一个异步函数。我们使用await关键字等待fetchData函数返回的Promise对象解析。在等待解析期间,函数将暂停执行,并等待异步操作完成。一旦异步操作完成并且成功解析,结果将被赋值给data变量,并可以在函数中进一步处理。如果异步操作失败,将会抛出一个异常,并被try/catch块捕获。


async/await的优势

简洁易读的代码

相比于Promise链式调用,使用async/await可以使异步代码更加简洁和易读。通过将异步操作的流程以同步的方式进行编写,代码的语义更加清晰,容易理解和维护。

错误处理的改进

使用async/await可以使错误处理变得更加直观和简单。通过使用try/catch块,我们可以捕获和处理异步操作中可能出现的错误。这样做不但使得代码的错误处理更加集中和可控,也使得错误信息的跟踪和调试更加容易。


Promise与async/await的对比

在这一部分,我们将对Promise和async/await进行更深入的比较,并探讨它们在代码结构、可读性和可维护性方面的优势。

首先,让我们来看看使用Promise和async/await处理异步操作的具体代码示例。假设我们需要从服务器上获取用户的个人信息、订单信息和账户信息,并在完成后将它们显示在页面上。

使用Promise

function getUserInfo() {
  return new Promise((resolve, reject) => {
    // 模拟从服务器获取用户信息的异步操作
    setTimeout(() => {
      const userInfo = {
        name: 'John Doe',
        age: 28,
        email: 'john.doe@example.com'
      };
      resolve(userInfo);
    }, 2000);
  });
}

function getOrderInfo(userId) {
  return new Promise((resolve, reject) => {
    // 模拟从服务器获取订单信息的异步操作
    setTimeout(() => {
      const orderInfo = {
        userId: userId,
        orderId: '123456',
        totalAmount: 100.00
      };
      resolve(orderInfo);
    }, 2000);
  });
}

function getAccountInfo(userId) {
  return new Promise((resolve, reject) => {
    // 模拟从服务器获取账户信息的异步操作
    setTimeout(() => {
      const accountInfo = {
        userId: userId,
        balance: 5000.00,
        creditLimit: 10000.00
      };
      resolve(accountInfo);
    }, 2000);
  });
}

getUserInfo()
  .then(userInfo => getOrderInfo(userInfo.userId))
  .then(orderInfo => getAccountInfo(orderInfo.userId))
  .then(accountInfo => {
    // 显示用户信息、订单信息和账户信息在页面上
    console.log('User Info:', userInfo);
    console.log('Order Info:', orderInfo);
    console.log('Account Info:', accountInfo);
  })
  .catch(error => {
    console.error(error);
  });

使用async/await

async function displayData() {
  try {
    const userInfo = await getUserInfo();
    const orderInfo = await getOrderInfo(userInfo.userId);
    const accountInfo = await getAccountInfo(orderInfo.userId);

    // 显示用户信息、订单信息和账户信息在页面上
    console.log('User Info:', userInfo);
    console.log('Order Info:', orderInfo);
    console.log('Account Info:', accountInfo);
  } catch (error) {
    console.error(error);
  }
}

displayData();

上述示例中,使用Promise链式调用的代码相对较长,需要通过多个.then()来处理多个异步操作。而使用async/await的代码更加简洁,通过await关键字将异步操作的结果保存到变量中,可以按照顺序依次执行异步操作并获取结果。

可读性和维护性对比

除了代码长度和结构上的差异之外,Promise和async/await在可读性和可维护性方面也有所不同。

使用Promise时,代码中经常出现多层嵌套的.then()回调函数,使得代码结构深度加深,变得难以理解和维护。尤其是在处理多个依赖的异步操作时,代码会变得非常复杂。在这种情况下,需要特别小心避免回调地狱的问题。

而使用async/await,则可以将异步操作的执行流程以同步的方式进行编写。代码的结构更加线性,易于理解和维护。可以像编写同步代码一样编写异步代码,提高了代码的可读性。

此外,使用async/await进行错误处理也更加直观和简单。在中,通常需要在每个.catch()中处理错误,而在async/await中,只需在try块中捕获错误,使得错误处理更加集中和可控。


结论

在本文中,我们对Promise和async/await进行了详细介绍,并比较了它们在代码结构、可读性和可维护性方面的优势。尽管Promise在处理异步操作方面提供了更高的灵活性,但async/await提供了更简洁、易读的代码写法和更好的错误处理方式。根据实际情况和个人偏好,选择合适的方式来处理异步操作至关重要。无论您选择使用Promise还是async/await,它们都是现代JavaScript异步编程的重要工具,能够显著提升代码的可读性和可维护性。当然我个人更喜欢使用async/await,因为它真的很方便。

接下来对整篇文章进行一个立体的总结:

Promise和async/await的简单对比
1.都是处理异步请求的方式
2.promise是ES6,async await 是ES7的语法
3.async await是基于promise实现的,他和promise都是非阻塞性的

优缺点:
1.promise是返回对象。我们要用then,catch方法去处理和捕获异常,并且书写方式是链式,容易造成代码重叠,不好维护,async await 是通过try catch进行捕获异常
2.async await最大的优点就是能让代码看起来像同步一样,只要遇到await就会立刻返回结果,然后再执行后面的操作。而promise的结果需要通过promise.then()的方式返回,会出现请求还没返回,就执行了后面的操作。

在这里插入图片描述


文章来源:https://blog.csdn.net/dyk11111/article/details/133753119
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。