news 2026/3/24 21:55:43

Intuitive Explanation of Async / Await in JavaScript

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Intuitive Explanation of Async / Await in JavaScript

原文:towardsdatascience.com/intuitive-explanation-of-async-await-in-javascript-730174c000bd

注意。本文已假设您熟悉回调、承诺,并对 JavaScript 中的异步范式有基本了解。

简介

异步机制是 JavaScript 以及编程中最重要的概念之一。它允许程序在后台单独执行次要任务,而不会阻塞当前线程执行主要任务。当次要任务完成时,其结果会被返回,程序继续正常运行。在这种情况下,这些次要任务被称为异步

异步任务通常包括向外部环境,如数据库、Web API 或操作系统发出请求。如果异步操作的结果不影响主程序的逻辑,那么在任务完成之前等待,就远不如不浪费这段时间并继续执行主要任务更好。

尽管如此,有时异步操作的结果会立即在下一行代码中使用。在这种情况下,后续的代码行应该在异步操作完成之前不应执行。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2d9e6146f116f86faf88ca8c593bb2b9.png

根据程序逻辑,某些异步请求可能会阻塞以下代码

注意。在进入本文的主要内容之前,我想提供为什么异步性被认为是数据科学中的一个重要主题,以及为什么我选择使用 JavaScript 而不是 Python 来解释async / await语法的动机。

01. 为什么在数据科学中关注异步性?

数据工程是数据科学不可或缺的一部分,主要涉及设计健壮和高效的数据管道。数据工程中的典型任务之一包括定期调用 API、数据库或其他数据源以检索数据、处理数据并将其存储在某个地方。

想象一个遇到网络问题无法立即返回请求数据的源数据。如果我们简单地在代码中向该服务发出请求,我们可能需要等待相当长的时间,而什么也不做。难道不是避免浪费宝贵的处理器时间并执行另一个函数会更好吗?这就是异步性的强大之处,这将是本文的核心主题!

02. 为什么选择 JavaScript?

没有人会否认这样一个事实:Python 是目前创建数据科学应用最受欢迎的选择。然而,JavaScript 也是一种拥有庞大生态系统、服务于各种开发目的的语言,包括构建从其他服务获取数据的网络应用程序。事实上,异步性在 JavaScript 中扮演着最基本的角色之一。

此外,与 Python 相比,JavaScript 对处理异步性提供了更丰富的内置支持,通常作为更深入探讨这一主题的更好例子。

最后,Python 也有类似的async / await构造。因此,本文中关于 JavaScript 的信息也可以转移到 Python 上,用于设计高效的数据管道。

JavaScript 中的异步代码

在 JavaScript 的早期版本中,异步代码主要是用回调来编写的。不幸的是,这导致开发者遇到了一个众所周知的问题,即“回调地狱”。很多时候,使用原始回调编写的异步代码导致了多层嵌套的代码作用域,这使得代码难以阅读。这就是为什么在 2012 年,JavaScript 的创造者引入了承诺

//Example of the"callback hell"problem functionOne(function(){functionTwo(function(){functionThree(function(){functionFour(function(){...});});});});

承诺为异步代码开发提供了一个方便的接口。承诺在构造函数中接受一个异步函数,该函数将在未来的某个时刻执行。在函数执行之前,承诺处于挂起状态。根据异步函数是否成功完成,承诺的状态将分别变为已解决拒绝。对于最后两种状态,程序员可以通过将.then().catch()方法与承诺链式调用,来声明如何在不同场景下处理异步函数的结果。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/cc0ce80879f7eb752cced38ea0253137.png

承诺状态图

除了这些,一组承诺可以通过使用组合方法如any()all()race()等“链”在一起。

承诺的不足

尽管承诺在回调的基础上有了显著的改进,但它们仍然不是完美的,原因有以下几点:

  1. 冗长性。承诺通常需要编写大量的样板代码。在某些情况下,由于其冗长的语法,创建一个具有简单功能的承诺可能需要几行额外的代码。

  2. 可读性。当多个任务相互依赖时,会导致承诺嵌套在另一个承诺中。这个臭名昭著的问题与“回调地狱”非常相似,使得代码难以阅读和维护。此外,在处理错误处理时,当错误通过多个承诺链传播时,通常很难追踪代码逻辑。

  3. 调试。通过检查堆栈跟踪输出,可能很难确定承诺内部错误的原因,因为它们通常不提供清晰的错误描述。

  4. 与旧库的集成。JavaScript 中许多旧库在过去是为了与原始回调一起工作而开发的,因此它们与 Promise 不太容易兼容。如果代码使用 Promise 编写,那么应该创建额外的代码组件以提供与旧库的兼容性。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/bd901b2172918a71fe949196be716515.png

回调和 Promise 都可能导致臭名昭著的 “回调地狱” 问题

Async / await

对于大多数情况,async / await构造被添加到 JavaScript 中作为对 Promise 的合成糖。正如其名所示,它引入了两个新的代码关键字:

  • async用于函数签名之前,标记函数为异步,它总是返回一个 Promise(即使没有显式返回 Promise,它也会被隐式包装)。

  • await用于标记为async的函数内部,并在代码中声明在返回 Promise 的异步操作之前。如果一行代码包含await关键字,那么在异步函数内部的后续代码行将不会执行,直到返回的 Promise 被解决(无论是处于fulfilledrejected状态)。这确保了如果后续代码行的执行逻辑依赖于异步操作的结果,那么它们将不会执行。

  • await关键字可以在异步函数内部使用多次。
  • 如果await在未标记为 async 的函数内部使用,将会抛出SyntaxError
  • 被标记为await的函数的返回结果是Promise 的解析值

下面的代码片段展示了async / await的使用示例。

//Async/awaitexample.//The code snippet prints startandend words to the console.function getPromise(){returnnew Promise((resolve,reject)=>{setTimeout(()=>{resolve('end');},1000);});}//since this functionismarkedasasync,it willreturna promiseasyncfunction printInformation(){console.log('start');const result=awaitgetPromise();console.log(result)//this line willnotbe executed until the promiseisresolved}

重要的是要理解,await 并不会阻塞主 JavaScript 线程的执行。相反,它只会挂起封装的异步函数(而其他程序代码可以在异步函数外部运行)。

错误处理

async / await构造提供了使用try / catch关键字进行错误处理的标准方式。要处理错误,必须将所有可能引发错误的代码(包括await声明)包裹在try块中,并在catch块中编写相应的处理机制。

实际上,使用try / catch块进行错误处理比在 Promise 中使用.catch()拒绝链更容易、更易读。

//Error handling template inside anasyncfunctionasyncfunction functionOne(){try{...const result=awaitfunctionTwo()}catch(error){...}}

Promise vs async / await

async / await是 Promise 的一个很好的替代品。它们消除了上述 Promise 的缺点:使用async / await编写的代码通常更易读、更易于维护,并且是大多数软件工程师的首选选择。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c5807c927b8b17b07b310d6273ca0976.png

async / await 的简单语法消除了 “回调地狱” 问题。

然而,否认承诺在 JavaScript 中的重要性是不正确的:在某些情况下,它们是一个更好的选择,尤其是在处理默认返回承诺的函数时。

代码可互换性

让我们看看一个用async / await和承诺编写的相同代码的示例。我们将假设我们的程序连接到数据库,并在建立连接的情况下请求有关用户的数据,以便在用户界面中进一步显示。

//Example of asynchronous requests handled byasync/awaitasyncfunction displayUsers(){try{const response=awaitconnectToDatabase();...const users=awaitgetData(data);showUsers(users);...}catch(error){console.log(`An error occurred:${error.message}`);...}}

这两个异步请求都可以很容易地通过使用await语法来包裹。在每个这些两个步骤中,程序将停止代码执行,直到检索到响应。

由于在异步请求过程中可能会发生错误(连接中断、数据不一致等),我们应该将整个代码片段包裹在try / catch块中。如果捕获到错误,我们将它显示到控制台。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/ce070fe7660df103cc10d2baf4e2b236.png

描述示例的活动图

现在,让我们用承诺来编写相同的代码片段:

//Example of asynchronous requests handled by promises function displayUsers(){...connectToDatabase().then((response)=>{...returngetData(data);}).then((users)=>{showUsers(users);...}).catch((error)=>{console.log(`An error occurred:${error.message}`);...});}

这个嵌套的代码看起来更冗长,更难阅读。此外,我们可以注意到每个 await 语句都被转换成了相应的then()方法,而 catch 块现在位于承诺的.catch()方法内部。

按照同样的逻辑,每个async / await代码都可以用承诺(promises)重写。这个陈述说明了async / await仅仅是承诺上的合成糖。

使用 async / await 编写的代码可以转换为承诺语法,其中每个 await 声明对应一个单独的.then()方法,异常处理将在.catch()方法中执行。

获取示例

在本节中,我们将查看async / await如何工作的真实示例。

我们将使用 REST 国家 API,该 API 通过以下 URL 地址提供请求国家的 JSON 格式的人口信息:https://restcountries.com/v3.1/name/$country

首先,让我们声明一个函数,该函数将检索 JSON 中的主要信息。我们感兴趣的是检索有关国家名称、首都、面积和人口的信息。JSON 以数组的形式返回,其中第一个对象包含所有必要的信息。我们可以通过访问具有相应名称的对象键来访问上述属性。

const retrieveInformation=function(data){data=data[0]return{country:data["name"]["common"],capital:data["capital"][0],area:`${data["area"]}km`,population:`{$data["population"]}people`};};

然后,我们将使用fetch API来执行 HTTP 请求。Fetch 是一个异步函数,它返回一个承诺。由于我们立即需要 fetch 返回的数据,我们必须等待 fetch 完成其工作后才能执行下面的代码行。为此,我们在 fetch 前使用await关键字。

//Fetch examplewithasync/awaitconst getCountryDescription=asyncfunction(country){try{const response=awaitfetch(`https://restcountries.com/v3.1/name/${country}`);if(!response.ok){throw new Error(`Bad HTTP status of the request(${response.status}).`);}const data=awaitresponse.json();console.log(retrieveInformation(data));}catch(error){console.log(`An error occurredwhileprocessing the request.nError message:${error.message}`);}};

同样地,我们在.json()方法之前放置另一个await,以解析代码中紧接着使用的数据。如果响应状态不佳或无法解析数据,则会抛出错误,然后在 catch 块中处理。

为了演示目的,让我们也通过使用承诺来重写代码片段:

//Fetch examplewithpromises const getCountryDescription=function(country){fetch(`https://restcountries.com/v3.1/name/${country}`).then((response)=>{if(!response.ok){throw new Error(`Bad HTTP status of the request(${response.status}).`);}returnresponse.json();}).then((data)=>{console.log(retrieveInformation(data));}).catch((error)=>{console.log(`An error occurredwhileprocessing the request.Error message:${error.message}`);});};

调用一个带有提供国家名称的函数将打印其主要信息:

//The result of calling getCountryDescription("Argentina"){country:'Argentina',capital:'Buenos Aires',area:'27804000 km',population:'45376763 people'}

结论

在本文中,我们介绍了 2017 年出现在 JavaScript 中的async / await构造。作为一种对承诺的改进,它允许以同步的方式编写异步代码,消除了嵌套代码片段。其正确使用与承诺结合,产生了一种强大的组合,使代码尽可能简洁。

最后,本文中关于 JavaScript 的信息对 Python 同样有价值,因为 Python 也有相同的async / await构造。个人而言,如果有人想深入了解异步编程,我建议更多地关注 JavaScript 而不是 Python。了解 JavaScript 中用于开发异步应用程序的丰富工具,可以更容易地理解其他编程语言中的相同概念。

资源

  • Async / await | 维基百科

除非另有说明,所有图像均为作者所有。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/15 8:40:11

GitHub镜像加速器推荐:提升HunyuanOCR代码克隆速度

GitHub镜像加速器推荐:提升HunyuanOCR代码克隆速度 在AI模型日益庞大的今天,开发者常常面临一个看似“基础”却异常棘手的问题——从GitHub拉取开源项目太慢。尤其是像腾讯推出的HunyuanOCR这类基于大模型的端到端OCR系统,其代码库和依赖资源…

作者头像 李华
网站建设 2026/3/22 19:32:55

留学生论文润色服务:先OCR识别扫描版再接入大模型修改

扫描论文秒变可编辑文档:OCR与大模型如何重塑留学生写作支持 在海外高校的深夜图书馆里,一个中国留学生正对着打印出来的论文草稿皱眉——导师用红笔圈出了几十处语法问题,但这份批注版是扫描件,无法直接修改。他要么手动逐字重打…

作者头像 李华
网站建设 2026/3/19 0:30:10

碳中和路线图制定:企业社会责任报告的支撑内容

碳中和路线图制定:企业社会责任报告的支撑内容 在“双碳”目标已成为全球共识的今天,越来越多的企业面临一个现实挑战:如何高效、准确地编制一份既符合国际标准又体现自身特色的碳中和路线图,并将其融入年度《企业社会责任报告》&…

作者头像 李华
网站建设 2026/3/23 6:12:15

基于Springboot家教预约管理系统【附源码+文档】

💕💕作者: 米罗学长 💕💕个人简介:混迹java圈十余年,精通Java、小程序、数据库等。 💕💕各类成品Java毕设 。javaweb,ssm,springboot等项目&#…

作者头像 李华
网站建设 2026/3/20 8:24:36

lora-scripts能否用于语音识别?探索其在ASR任务中的潜在应用场景

lora-scripts能否用于语音识别?探索其在ASR任务中的潜在应用场景 在医疗门诊室里,医生一边问诊一边口述病历,系统自动将对话转为结构化电子记录——这听起来像是AI的高阶应用。但现实是,通用语音识别模型面对“阿司匹林”“冠状动…

作者头像 李华
网站建设 2026/3/15 11:23:31

1.25 大模型API使用实战:OpenAI、DeepSeek、通义千问API调用详解

1.25 大模型API使用实战:OpenAI、DeepSeek、通义千问API调用详解 引言 掌握大模型API调用是使用AI能力的基础。本文将实战演示如何调用OpenAI、DeepSeek、通义千问等主流大模型的API,帮你快速上手AI应用开发。 一、API调用基础 1.1 通用流程 #mermaid-svg-EjliH7Mgzc4Vsn…

作者头像 李华