异步 JavaScript——回调、承诺和异步等待的工作原理 回调函数 承诺 异步等待 浏览器/Node 支持

2025-05-25

异步 JavaScript——回调、Promises 和 Async-Await 的工作原理

回调函数

承诺

异步等待

浏览器/节点支持

如果您喜欢这篇文章,请给它一个💓、🦄或🔖!

JavaScript 推崇异步编程的一项特性。这意味着,如果任何操作需要一段时间,你的程序可以在该操作完成之前继续执行其他操作。一旦该操作完成,你就可以对结果进行一些处理。这对于数据获取等功能来说是一个很棒的特性,但对于新手来说可能会造成困惑。在 JavaScript 中,我们有几种不同的方法来处理异步:回调函数、Promises 和 async-await。


我制作了其他易于理解的教程内容!请考虑:


回调函数

回调函数是你提供的函数,将在异步操作完成后执行。让我们创建一个模拟的用户数据获取器,并使用回调函数来处理结果。

虚假数据获取器

首先,我们创建一个不接受回调函数的伪数据获取器。由于它fakeData在 300 毫秒内不存在,因此我们无法同步访问它。

const fetchData = userId => {
  setTimeout(() => {
    const fakeData = {
      id: userId,
      name: 'George',
    };
    // Our data fetch resolves
    // After 300ms. Now what?
  }, 300);
};
Enter fullscreen mode Exit fullscreen mode

为了能够真正地对我们的数据做一些事情fakeData,我们可以将fetchData一个引用传递给一个将处理我们的数据的函数!

const fetchData = (userId, callback) => {
  setTimeout(() => {
    const fakeData = {
      id: userId,
      name: 'George',
    };
    callback(fakeData);
  }, 300);
};
Enter fullscreen mode Exit fullscreen mode

让我们创建一个基本的回调函数并测试它:

const cb = data => {
  console.log("Here's your data:", data);
};

fetchData(5, cb);
Enter fullscreen mode Exit fullscreen mode

300 毫秒后,我们应该看到以下记录:

Here's your data: {id: 5, name: "George"}
Enter fullscreen mode Exit fullscreen mode

承诺

Promise 对象表示 JavaScript 中操作的最终完成。Promise 可以是resolvereject。当 Promise 解析成功时,你可以使用 then 方法处理其返回值。如果 Promise 被拒绝,你可以使用 catch 方法捕获错误并进行处理。

Promise对象的语法如下:

new Promise(fn);
Enter fullscreen mode Exit fullscreen mode

Werefn是一个接受一个resolve函数和一个可选reject函数的函数。

fn = (resolve, reject) => {};
Enter fullscreen mode Exit fullscreen mode

虚假数据获取器(带有承诺)

让我们使用和之前一样的伪数据获取器。我们不传递回调函数,而是返回一个新Promise对象,该对象会在 300 毫秒后解析用户数据。此外,我们还可以给它一个很小的拒绝概率。

const fetchData = userId => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() < 0.1) {
        reject('Fetch failed!');
      }
      const fakeData = {
        id: userId,
        name: 'George',
      };
      resolve(fakeData);
    }, 300);
  });
};
Enter fullscreen mode Exit fullscreen mode

我们的新fetchData功能可以按如下方式使用:

fetchData(5)
  .then(user => {
    console.log("Here's your data:", user);
  })
  .catch(err => {
    console.error(err);
  });
Enter fullscreen mode Exit fullscreen mode

如果fetchData成功解析(90% 的情况下都会发生这种情况),我们将像回调解决方案一样记录用户数据。如果被拒绝,我们将返回console.error我们创建的错误消息(“获取失败!”)。

Promises 的一个优点是你可以串联执行后续的 Promises。例如,我们可以这样做:

fetchData(5)
  .then(user => {
    return someOtherPromise(user);
  })
  .then(data => {
    console.log(data);
  })
  .catch(err => {
    console.error(err);
  });
Enter fullscreen mode Exit fullscreen mode

此外,我们可以传递一个 Promises 数组,以便Promise.all在所有 Promises 都得到解决后才采取行动:

Promise.all([fetchData(5), fetchData(10)])
  .then(users => {
    console.log("Here's your data:", users);
  })
  .catch(err => {
    console.error(err);
  });
Enter fullscreen mode Exit fullscreen mode

在这种情况下,如果两个 Promise 都成功解决,则会记录以下内容:

Here's your data:
[{ id: 5, name: "George" }, { id: 10, name: "George" }]
Enter fullscreen mode Exit fullscreen mode

异步等待

Async-await 提供了一种不同的 Promise 编写语法,有些人觉得它更清晰。使用 async-await,你可以创建一个async函数。在该异步函数中,你可以await在执行后续代码之前获取 Promise 的结果!让我们来看看数据获取的示例。

const fetchUser = async userId => {
  const user = await fetchData(userId);
  console.log("Here's your data:", user);
};
fetchUser(5);
Enter fullscreen mode Exit fullscreen mode

很不错吧?不过还有一点小问题:我们没有处理 Promise 被拒绝的情况。我们可以用 来处理try/catch

const fetchUser = async userId => {
  try {
    const user = await fetchData(userId);
    console.log("Here's your data:", user);
  } catch (err) {
    console.error(err);
  }
};
fetchUser(5);
Enter fullscreen mode Exit fullscreen mode

浏览器/节点支持

由于回调函数只是传递给其他函数的普通函数,因此无需担心支持问题。Promises 自 ECMAScript 2015 以来就已成为标准,并获得了良好的支持,但 Internet Explorer 尚未支持。Async-await 较新(自 ECMAScript 2017 以来成为标准),并且在较新的浏览器版本中得到了良好的支持。同样,Internet Explorer 不支持它。

在节点方面,async-await(以及 Promises)自 nove v7.6 以来得到了很好的支持。

文章来源:https://dev.to/nas5w/asynchronous-javascript-how-callbacks-promises-and-async-await-work-1f7p
PREV
学习 Redux 基础知识,30 行代码编写自己的 Redux 版本 Redux 能做什么?Redux 的基本原理 哎呀,我应该理解所有这些吗? 滚动开发我们自己的 Redux 测试运行 差不多就是这样了
NEXT
基于常见混淆或误解领域的 JavaScript 技巧集合 内容贡献