Async 和 Await 是 ECMAScript 2017 (ES8) 中引入的 JavaScript 关键字,可以以更易读、类似同步且易于管理的方式编写异步代码。它们简化了需要时间才能完成的处理操作,例如从 API 获取数据。
在深入研究之前,我们首先了解 JavaScript 中同步和异步编程的概念。在同步编程中,任务按照它们出现的顺序一个接一个地顺序执行。每项任务必须在下一项任务开始之前完成。另一方面,异步编程允许任务在后台运行,使 JavaScript 能够继续执行其他任务,而无需等待前面的任务完成。
众所周知,JavaScript 是一种单线程语言,这意味着它一次只能执行一个任务。如果是这样的话,JavaScript 如何处理异步代码呢?这是通过事件循环实现的,事件循环是与 JavaScript 运行时环境一起工作的关键机制。事件循环使异步操作能够在不阻塞主线程的情况下运行,从而确保 JavaScript 保持响应能力。事不宜迟,喝杯咖啡,我们就进入今天的正题吧!
为了更好地理解这个概念,我们将采取更实际的方法。在引入 async 和 wait 之前,Promise 是使用 ES6 (ECMAScript 2015) 中引入的“旧方式”处理的。让我们探索下面的示例。
上面的代码演示了处理 Promise 的传统语法。 Promise 构造函数用于创建一个新的 Promise 实例。它接受一个函数(称为执行器函数)作为其参数,其中包括两个参数:resolve 和reject。该执行器函数包含异步操作的逻辑。在此示例中,会立即调用resolve,表示 Promise 已成功完成并具有特定值。一旦 Promise 被解析,.then 方法就会被触发,执行它的回调来记录结果。
但是,这种语法可能有点难以记住。 async/await 的引入简化了处理 Promise 的过程,使其更容易阅读和理解。让我们看下面的例子。
为了实现异步函数,我们使用 async 关键字,它告诉 JavaScript 这不是一个常规函数,而是一个异步函数。第二个示例展示了如何使用箭头函数完成相同的操作。
另一个需要注意的重要概念是await 关键字。 async 和await 一起工作来简化Promise 的处理。 wait 关键字只能在异步函数内部使用,不能在函数外部或常规函数内部使用。它必须始终出现在标记为异步的函数中。现在我们已经介绍了基础知识,让我们更深入地了解这个概念!
许多 JavaScript 开发人员在他们的代码中经常使用 async/await,但只有少数人真正了解它的幕后功能。这就是本教程的用武之地。让我们探索一个示例来分解它。
在这个例子中,我们使用 .then 方法来更好地理解 Promise 与 async/await 方法相比是如何工作的。当调用handlePromise()函数时,代码会逐行执行。当 JavaScript 遇到 .then 方法时,它会将回调注册到微任务队列并立即移动到下一行,打印“hello world”。
所有同步任务完成后,JavaScript 引擎将检查微任务队列中是否有待处理的任务。五秒后,setTimeout 完成,其回调被推回调用堆栈。此时,Promise 已解决,注册的回调将运行,记录结果。
简而言之,JavaScript 引擎不会等待,它直接移动到下一行代码。现在,使用 async/await 时是否适用相同的行为,或者它的工作方式是否不同?让我们来了解一下吧!
在上面的示例中,当调用handlePromise()函数时,会打印第一行“the start”。然后 JavaScript 遇到了await关键字,它表明该函数是异步的并且涉及到一个Promise。它表明由于 setTimeout,Promise 将需要五秒钟才能解决。此时,handlePromise()函数被暂停(从调用堆栈中删除),并且函数内await之后的任何代码都被暂停。
JavaScript 引擎继续执行程序的其余部分。五秒后,Promise 被解析,挂起的函数返回到调用堆栈,handlePromise() 中的剩余行'Promise issolved' 和 'the end' 依次执行。
需要注意的是,挂起函数不会阻塞主线程。如果在handlePromise()函数之外编写了其他代码,它将在Promise等待解析时执行。
下面的示例演示了此行为的实际效果:
在此示例中,第一个输出是开始。当 JavaScript 遇到 wait 关键字时,它会识别出 Promise 需要 5 秒才能解析。此时,函数被挂起,JavaScript 继续执行函数外部的任何代码。结果,接下来打印的是我们在外面。
一旦 Promise 在五秒后被解析,handlePromise()函数就会恢复到调用堆栈,并执行其剩余的行,打印 Promise 被解析,然后结束。
让我们再看一个示例,我们将尝试在其他示例中使用 async/await 进行 API 调用,以更好地理解这一概念。
在上面的代码中,执行过程遵循前面讨论的相同原则。当 JavaScript 遇到 fetch 函数时,它会挂起 getData() 函数并等待 fetch 调用返回响应对象,该对象包含响应的状态、标头和正文等各种属性。一旦响应可用,该函数就会恢复执行。
响应正文是我们需要的数据,但它是原始形式(例如文本或二进制)并且不能立即使用。为了将其转换为 JavaScript 对象以便于操作,我们使用 .json() 方法来解析原始 JSON 响应。这个过程涉及到另一个Promise,这就是为什么需要第二次await。该函数再次挂起,直到 Promise 解决。
一旦两个 Promise 都得到满足,getData() 函数就会恢复,解析后的数据会打印到控制台。这是解释 fetch 工作原理的简单方法,不是吗?现在,回到我们的主要讨论!如果我们的 API 响应失败怎么办?我们如何使用 async/await 来管理错误?让我们在下一节中深入探讨这一点。
传统上,Promise 中的错误是使用 .catch 方法处理的。但是使用 async/await 时我们如何处理错误呢?这就是 try...catch 块发挥作用的地方。
在上面的代码中,Promise 包含在 try 块中,如果 Promise 成功解析,该块就会执行。但是,如果 Promise 被拒绝,则会在 catch 块中捕获并处理错误。
但是您知道我们仍然可以用传统方式处理错误吗?这是一个例子:
要使用传统方法处理 async/await 中的错误,只需将 catch 方法附加到函数,如上所示。它的功能与 try/catch 块相同。
Async/await 彻底改变了 JavaScript 处理异步操作的方式,与 .then 和 .catch 等传统方法相比,使代码更具可读性且更易于管理。通过利用 async 和 wait,我们可以编写感觉更加同步的异步代码,从而提高整体代码的清晰度。在了解内部工作原理(例如事件循环和微任务队列)的同时,实现 async/await 对于现代 JavaScript 开发来说是简单且高效的。通过使用 try/catch 或 .catch 进行正确的错误处理,我们可以自信地管理成功和失败的 Promise。
感谢您的坚持!我希望这篇文章能让您对 async/await 更加清楚。祝您在编码冒险中取得成功——去构建一些令人惊奇的东西!
以上是JavaScript 开发人员 Aysnc 和 Await 实用指南的详细内容。更多信息请关注PHP中文网其他相关文章!