在 ES9 中使用 async/await 统一异步的编程模型!

阅读时长 8 分钟读完

在以前的 JavaScript 版本中,开发者使用回调函数或者 Promise 来实现异步编程。但是这种编程方式有时会变得非常复杂和难以调试。随着 ECMAScript 9 的发布,JavaScript 开发人员可以使用 async/await 来简化异步编程,使代码更加易读易写。

本文将介绍 async/await 的使用方式,并讲述一些最佳实践。

什么是 async/await?

async/await 是 ECMAScript 7 中引入了异步编程的一种新方式,它将异步的流程变得像同步一样的写法。async/await 依靠 Promises 来工作,它使得异步操作在代码中以同步的风格展现,并且提供了更加简单、容易理解的编程模型。

async/await是两个关键字:

  • async - 函数标记为异步函数
  • await - 等待异步操作完成

当调用一个异步操作时,执行控制权交给了这个异步操作,并且执行异步操作的代码将继续执行。在这个过程中,被标记为 async 的函数不会阻塞,而是会立即返回一个 Promise 对象。异步操作完成后会继续执行这个 async 函数,并且将异步结果包含在 Promise 对象中返回。

让我们来看一个简单的例子:

在这个例子中,我们定义了一个名为 getData 的函数,它标记为 async。这个函数使用 fetch API 发起了一个 HTTP 请求。await 关键字暂停了函数的执行,直到 fetch 请求完成并返回数据。在这之后,我们将数据解析为 JSON 格式并返回一个 Promise 对象。

在我们调用 getData() 的时候,这个函数立即返回一个 Promise 对象。在异步操作结束之后,Promise 对象将被解析为我们所需的数据,并被传递给 then

编写简单易懂的异步程序

async/await 简化了异步编程,让代码变得简单易懂。例如,我们可以将一个 Promise 链写成如下所示的代码:

使用 async/await 语法,我们可以将这个 Promise 链转换为一个结构更加清晰、易于阅读、并且更容易处理错误的代码:

-- -------------------- ---- -------
----- -------- --------- -
  --- -
    ----- -------- - ----- -------------------
    ----- ---- - ----- ----------------
    ------------------
  - ----- ------- -
    ---------------------
  -
-

----------

with 异步函数(async function)的开发者可以使用 try-catch 块捕获异步操作的异常,并且是用 Promise 链实现的同时能够获得更好的可读性。

最佳实践

以下是一些在使用 async/await 时值得关注的最佳实践。

1. 让异步操作以串行方式执行

async/await 允许你将异步操作写成一个类似于同步代码的方式。但是在这个过程中,我们需要非常小心地处理异步操作的执行顺序,避免让多个异步操作同时进行。

以这个简单的例子为例,我们可以很容易地把两个异步任务定义在同一个异步函数中:

在这个函数中,我们依次执行了两条异步请求。需要注意的重点是,我们在获取用户信息之后才会请求相应的数据。这种顺序对于保持异步操作的同步执行非常重要。

如果我们使用传统的 Promise 风格来编写这个例子,代码将变得更加难懂:

-- -------------------- ---- -------
-------- ------------- -
  --- ----- -----
  ------ ------------------
    -------------- -- ----------------
    ------------ -- -
      ---- - -------
      ------ ------------------------------
    --
    -------------- -- ----------------
    ------------ -- -
      ---- - -------
      ----------------- ------
    --
    ------------ -- ----------------------
-

异步编程非常容易被写成回调风格或者 Promise 风格,但是使用 async/await 可以帮助我们减轻这种程度并且保持代码的易读性。

2. 使用 asyn/await 写代码的时候尽量精细化拆分

当我们在使用 async/await 时,为了保持一致性和可维护性,建议我们将异步操作的各个部分都划分得足够细致。一个方法(method)能够执行的最好限制是仅仅执行一个异步操作。如果不能够满足这种限制,可以将这个方法写成与上下文环境无关的函数。

例如,这个函数同时发送了多个 webhooks:

-- -------------------- ---- -------
----- -------- ------------------- -
  ----- ----- - ----- --------------------
    -------------- -- -----------------
  ------------------- ---- -- -
    ----- ---------------- - ------- ---------------
    ----- -------------------------------------- -
      ------- -------
      ----- ---------------- -------- ---------------- ---
      -------- - --------------- ------------------ -
    ---
  ---
-

这个函数发送了多个异步请求,这些请求相互独立,可以异步进行。但是这里存在一个问题:因为 users.forEach 中的回调函数是异步的,每次执行都会产生一个新的 Promise 实例。调用者无法知道这些 Promise 实例何时解析。

解决这个问题的方法是为每个异步请求都创建一个单独的 async 函数。在这个过程中,父函数使用 Promise.all 来等待所有的异步请求完成。

-- -------------------- ---- -------
----- -------- ---------------------------------- ----- -
  ------ -------------------------------------- -
    ------- -------
    ----- ---------------- -------- ---------------- ---
    -------- - --------------- ------------------ -
  ---
-

----- -------- ---------------------------- -
  ----- ----- - ----- --------------------
    -------------- -- -----------------
  ----- -------------------- - -------------- --
    ------------------------ --------------- -------
  ----- ----------------------------------
-

在这个重构后的代码中,我们创建了一个带有一个 user 参数的函数 sendNotification。然后,我们使用 users.map 迭代用户列表,并为每个用户调用 sendNotification。在这个过程中返回的 Promise 每个都通过 Promise.all 方法来等待所有请求成功和失败。 这个代码结构更加清晰,易于阅读和维护。在这个过程中,我们也不会创建过度的 Promise 实例,而是使用类似 Promise.all 这样的工具来等待所有异步请求完成。

结论

async/await 是 JavaScript 异步编程新的标准,它提供了比传统的回调方式和 Promise 更加易用的编程风格。让我们以更清晰的方式表达异步流程,并允许我们在函数中使用 try-catch 来处理错误。

在使用 asyn/await 时,我们需要将异步操作划分得非常微粒化,在组合这些小的、可重用的异步函数进行使用。这样的做法可以使我们的代码更具一致性、可维护性和适应性。

使用async/await编写异步操作的代码不仅更加易读、可维护性和适应性,而且还有助于调试和处理错误(error handling)。异步编程失败时对应的Stack Trace打印错误对于调试非常友好。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67455e46c1a23897ea92d833

纠错
反馈