JavaScript 异步编程:Promise、await 和 async

15 阅读4分钟

异步编程的需求

在编写 JavaScript 代码时,经常会遇到需要执行异步操作的情况。例如,从服务器获取数据、处理用户输入或者执行耗时的计算都可能是异步操作。在 JavaScript 中,通常有两种方法来处理异步操作:回调函数和 Promise。

回调函数

回调函数是 JavaScript 中最早用于处理异步操作的方法之一。它的工作方式是将一个函数作为参数传递给另一个函数,并在异步操作完成后调用这个回调函数。这是一个简单的示例:

function fetchData(callback) {
  setTimeout(function () {
    const data = "Hello, world!";
    callback(data);
  }, 1000);
}

function processData(data) {
  console.log("Data received: " + data);
}

fetchData(processData);

在这个示例中,fetchData 函数模拟了一个异步操作(使用 setTimeout),并在操作完成后调用了 processData 回调函数。

尽管回调函数是一种处理异步操作的有效方法,但它存在一些问题。首先,当需要执行多个异步操作时,回调函数可能会导致回调地狱的情况,代码难以理解和维护。其次,回调函数容易陷入回调嵌套,使得代码变得混乱。这种情况通常称为回调地狱,如下所示:

asyncOperation1(function () {
  asyncOperation2(function () {
    asyncOperation3(function () {
      // ...
    });
  });
});

为了解决这些问题,Promise、await 和 async 这些功能被引入到 JavaScript 中。

Promise

Promise 是一种表示异步操作的对象,它可以处于三种状态之一:pending(进行中)、fulfilled(已完成)或 rejected(已拒绝)。Promise 提供了一种更结构化和可控的方式来处理异步操作,同时避免了回调地狱的问题。

创建 Promise

要创建一个 Promise,可以使用 Promise 构造函数,并传递一个执行异步操作的函数作为参数。这个函数通常有两个参数:resolvereject,它们是两个函数,用于表示异步操作的结果。

const myPromise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const randomValue = Math.random();
    if (randomValue < 0.5) {
      resolve(randomValue);
    } else {
      reject("Promise rejected");
    }
  }, 1000);
});

在上面的示例中,myPromise 是一个 Promise 对象,它表示一个异步操作,该操作在1秒后返回一个随机数。如果随机数小于0.5,Promise 将被解决(fulfilled),否则将被拒绝(rejected)。

处理 Promise

一旦创建了一个 Promise,我们可以使用 .then() 方法来处理它的结果,或者使用 .catch() 方法来处理拒绝的情况。这使得代码更加清晰和易于阅读。

myPromise
  .then((result) => {
    console.log("Promise fulfilled with result: " + result);
  })
  .catch((error) => {
    console.error("Promise rejected with error: " + error);
  });

在上面的示例中,如果 Promise 被 resolve,将会调用 .then() 中的回调函数,如果 Promise 被 reject,将会调用 .catch() 中的回调函数。

Promise 链

Promise 还允许我们构建 Promise 链,通过在 .then() 方法中返回另一个 Promise,我们可以按顺序执行多个异步操作。

function fetchUserData() {
  return fetch("https://api.example.com/user")
    .then((response) => response.json())
    .then((data) => {
      console.log("User data:", data);
      return data;
    })
    .catch((error) => {
      console.error("Error fetching user data:", error);
      throw error;
    });
}

async 和 await

尽管 Promise 提供了一种更好的方式来处理异步操作,但它的语法依然比较繁琐,尤其是当需要依赖多个异步操作的结果时。为了解决这个问题,JavaScript 引入了 async 和 await 关键字,它们可以让异步代码看起来更像同步代码。

async 函数

要定义一个异步函数,只需在函数前面加上 async 关键字。异步函数可以包含一个或多个 await 表达式,这些表达式用于等待 Promise resolve。

async function fetchData() {
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();
  return data;
}

在上面的示例中,fetchData 函数是一个异步函数,它使用 await 表达式等待 fetchjson 方法返回的 Promise 解决。这使得异步代码看起来更加线性,而不是嵌套的回调函数。

总结

JavaScript 的异步编程是现代 web 开发中的重要部分。Promise、await 和 async 这些功能使得异步代码更加清晰和可维护,同时避免了回调地狱的问题。Promise 提供了一种更结构化的方式来处理异步操作,而async 和 await 关键字则使异步代码看起来更像同步代码。