- Async/await 依靠 Promise 来提供更易读的异步代码,更接近传统的同步风格。
- 异步函数总是返回一个 Promise,并允许你使用 await 来暂停它们的执行,而不会阻塞主线程。
- 使用 try/catch 和 Promise.all 等工具进行错误处理,可以更容易地控制故障并并行组合多个操作。
- Promise、async/await 和 fetch 等 API 的结合使用是现代面向网络的 JavaScript 开发的基础。
La 编程 JavaScript 中的异步 在现代 Web 开发中,API 请求、访问已成为不可或缺的一部分。 数据库文件读取或任何耗时超过一瞬间的操作都依赖于它来保持界面流畅运行。感谢它 Promise、async/await 和 API 我们拥有一套非常强大的工具,可以在不阻塞应用程序的情况下控制该流程。
多年来,这种模式主要被使用。 回调函数和链式承诺虽然它们效果很好,但最终可能会陷入臭名昭著的“回调地狱”或无休止的循环。 .then() y .catch() 难以理解。随着……的到来 ECMAScript 2017 中的 async/awaitJavaScript 在可读性方面取得了巨大进步,使得异步代码的编写方式几乎与传统的同步代码完全相同。
JavaScript 中的异步编程是什么?它为什么如此重要?
La 异步编程 这是一种组织代码的方式,使得任务可以开始执行,而程序可以继续执行其他操作,而不会因为等待响应而“冻结”。在 JavaScript 中,这一点至关重要,因为无论是在浏览器还是 Node.js 中,该语言都是在……上运行的。 单主线程 它兼顾了逻辑和接口两方面。
当你扔东西时 向 API 发送 HTTP 请求读取文件或查询数据库时,这些操作可能需要几毫秒到几秒的时间。如果是同步操作,用户会看到页面被锁定,无法进行交互。而异步模型则允许 JavaScript 引擎将这些任务委托给其他进程,并在此期间继续执行其他操作。 绘制用户界面、处理事件和运行其他脚本.
最初,操作结束时的标准反应机制是传递一个 回调函数 作为参数。该函数稍后会执行,并返回结果或错误。问题在于,通过在回调函数中嵌套回调函数,会导致可怕的…… 回调地狱代码缩进过多,难以阅读、调试和维护。
为了解决部分混乱局面,出现了以下措施: 承诺它们封装了异步操作的结果,并允许将操作链接在一起。 .then() 并利用以下方式管理故障 .catch()这已经是一个很大的改进了,但代码中仍然充满了“伪装”在这些方法后面的回调函数。
Promise:async/await 的基础
在理解 async/await 之前,首先必须非常清楚 async/await 是什么。 JavaScript 中的 PromisePromise 是一个对象,它表示异步操作的未来结果:尚未发生但稍后会发生的事情,无论成功与否。
一 标准承诺 可以处于以下三种明确定义的状态之一:待定(有待)手术进行中,已完成(完成)当它以良好的结果结束并产生价值时,或者被拒绝时(拒绝当它失败并产生错误时。 承诺的状态 决定接下来要执行哪些回调函数。
当您使用以下方式创建承诺时 构造函数 Promise你向它传递一个可执行函数,该函数接收两个参数: resolve y reject如果手术顺利,你就打电话 resolve(valor)如果出现问题,您可以调用 reject(error)然后,从外部,你可以使用 miPromesa.then() 处理结果和 .catch() 管理错误。
除了精心设计的承诺之外,许多现代语言 API 已经 他们兑现了一系列承诺。。 功能介绍 fetch()例如,它会发起一个 HTTP 请求,并立即返回一个 Promise,该 Promise 最终会解析为一个对象。 Response 如果网络出现故障,则会被拒绝。
为了并行处理多个任务,JavaScript 提供了一些实用工具,例如…… Promise.all()此方法接收一个 Promise 数组,并返回一个新的 Promise,该 Promise 仅在所有 Promise 都成功完成时才会解析,如果其中任何一个 Promise 失败,则会被拒绝。这在以下情况下尤其有用: 整合来自不同来源的数据 在进一步之前。
关键概念:async、await 和错误模型
的语法 async/await 是建立在 Promise 之上的。它并不会取代它们。它所做的,是提供一种更便捷、更清晰的方式,更接近于同步编程,来使用它们,从而减少视觉干扰。 .then() 被锁链束缚。
关键词 async 它放在函数前面,告诉 JavaScript 引擎该函数将异步执行,并且无论函数内部发生什么, 永远信守承诺即使你返回一个普通值,引擎也会自动将其包装起来。 Promise.resolve(valor).
就其本身而言,保留词 await 只能在声明为函数的函数中使用。 async它的功能是暂停该函数内的执行,直到传递给它的 Promise 得到满足。 已解决或已拒绝直接返回已解析的值,或者如果 Promise 以错误结束,则抛出异常。
宽多美国 await promesa 如果 Promise 被解析,你会得到与同步调用相同的结果。相反,如果 Promise 被拒绝, 此时会生成一个异常。相当于做了 throw error这样就可以重用与同步代码中相同的错误处理结构,例如: try..catch.
通过将异步调用包装在一个代码块中。 try { ... } catch (error) { ... }我们可以捕获任何承诺失败的情况,向用户显示适当的消息,或者做出诸如重试、重定向或将失败记录到外部系统之类的决定。
关键字 async:始终返回 Promise 的函数。
地方 async 在函数前面 这会产生两个非常重要的后果:一方面,它强制返回值始终为 Promise;另一方面,它使得使用 await 在该函数内部,它完全改变了异步流程的结构方式。
如果你声明类似这样的内容 async function sumar() { return 1; }虽然它看起来像是一个普通的功能,但实际上你得到的是…… 返回一个已解析 Promise 的函数 鼓起勇气 1在您的系统之外,您需要使用以下方式访问该结果: await sumar() 或 sumar().then() 如果你喜欢链式风格的话。
你也可以从函数中显式地返回一个 Promise。 async, 例如 return Promise.resolve(1);行为将相同。在这两种情况下,该函数都符合 Promise 模型,这使得它能够与其他异步函数和实用程序(例如)结合使用。 Promise.all().
刚开始的时候,我们经常会忘记将函数标记为这样。 async 然而我们却使用 await 在在这种情况下,JavaScript 会抛出一个语法错误,表明 await 这仅在异步函数(或更高层级支持异步函数的模块)中有效。此消息旨在提醒您: async y await 他们总是齐头并进。
在不使用模块或无需支持旧版浏览器的环境中,普遍存在一种模式,该模式包括: 将代码封装在一个自执行的异步函数中, 就像是 (async () => { ... })();这样,整个代码块就保持在异步上下文中,而不会“污染”全局空间。
await 的工作原理以及它对 Promise 的实际作用
真正的思维转变来自于…… await当 JavaScript 引擎遇到 const resultado = await algunaPromesa; 在函数内部 async, 在该点暂停该函数的执行。 然后返回事件循环继续处理其他任务。
与此同时,承诺继续独立执行。当它最终完成时,引擎会紧接着执行包含以下代码的行之后的函数。 await,赋值给变量 Promise 返回的值从阅读代码的人的角度来看,这看起来像是一个同步调用,只是耗时稍长一些。
有趣的是,这种停顿 它不会阻塞CPU主 JavaScript 线程可以处理用户事件、动画、其他异步代码等等。这就是我们说……的原因。 await 它更像是一种 优雅的句法抽象 关于一种在后台运行任务的新方式所带来的好处。
如果你愿意,你可以想象一下这种组合 const resultado = await promesa; es promesa.then(valor => { resultado = valor; ... }) 它具有等效性,但以线性方式分发,避免了嵌套回调,提高了流程清晰度。
一个有趣的细节是,如果你搬到 await 一个不是 Promise 但具有方法的对象 .then()引擎会尝试将其视为真实存在。它会调用该函数。 .then() 具有两个内部功能 决议和拒绝 与建造者非常相似 Promise。 这样啊 await 它与“thenable”类型承诺的实现兼容。
使用 async/await 进行错误处理:try/catch 和被拒绝的 Promise
当一个承诺是 拒绝使用 await直接后果是该行代码会抛出异常。从你的代码来看,就好像你写了…… throw error; 就在那里 await这会导致执行跳转到该代码块。 catch 最接近。
例如,如果您使用 const datos = await obtenerDatos(); 和功能 obtenerDatos 它返回一个以错误告终的 Promise;流程将直接转到 块 catch (error) 你的 try..catch如果没有捕获块将其包裹起来,则隐式承诺该函数返回 async 将被拒绝。
这意味着,尽管其中可能仍有承诺,但当你与……合作时 async/await 是处理错误的自然方式 它与 try..catch就像其他任何抛出异常的 JavaScript 代码一样。这种风格的统一极大地简化了逻辑,并减少了根据代码是同步还是异步而记住不同模式的需要。
如果你不小心忘记添加一个 .catch() 函数返回的 Promise async 如果失败,环境将显示警告。 “未妥善管理的承诺” (未处理的承诺拒绝此外,您还可以使用浏览器中的全局事件。 unhandledrejection 注册一个处理程序,用于捕获所有这些错误,防止它们被忽略。
当你结合时 await 使用诸如 Promise.all承诺中的任何一个错误都会造成严重后果。 传播共同的承诺 最终会抛出一个异常,你可以用以下方式捕获该异常: try..catch这样一来,复杂工作流程中的错误管理就能保持一致性和集中性。
使用 fetch 的 Async/await:API 调用步骤详解
async/await 最常见的用途之一是访问 使用函数的 HTTP API fetch()此 API 返回一个 Promise,该 Promise 会解析为一个对象。 Response然后,您可以对其调用诸如以下的方法: .json() o .text()而这些承诺又会随之做出新的承诺。
假设你想把从端点请求数据的逻辑封装到一个函数中。如果你声明 async function obtenerDatos()在其中你可以 等待网络响应 await fetch(...) 然后使用以下命令将正文转换为 JSON 格式 const datos = await respuesta.json(); 无需串联多个 .then().
为确保服务器响应正确,通常会检查该属性。 respuesta.ok这表示 HTTP 状态代码是否在 200-299 范围内。如果不在范围内,您可以手动抛出错误。 throw new Error('Error en la red');这将导致 catch 记录情况。
组织整个流程 try { ... } catch (error) { ... } 这使得异步函数更具弹性:任何问题,无论是网络问题、JSON 解析问题还是逻辑问题,都会触发捕获块,您可以在其中进行处理。 记录错误或显示更易于理解的消息.
最后,要使用该函数,只需像调用其他函数一样调用它即可: obtenerDatos();请记住,由于它是异步的,所以它会返回一个 Promise,因此您需要从外部选择是否使用它。 await obtenerDatos() 在另一个函数中 async 或者用……处理 .then().catch() 如果您处于非异步环境中。
使用 Promise.all 和 async/await 并行执行多个操作
通常情况下,你并不想等待一个请求完成才开始下一个;相反,你更感兴趣的是…… 一次启动多个异步操作 等他们都完成后再继续。为此,该方法 Promise.all() 它非常合身。
假设你需要使用两个不同的 API,例如 /datos1 y /datos2, 然后 将信息合并成一个单一对象与其等待第一个程序完成再开始第二个,不如这样写: const [r1, r2] = await Promise.all([fetch(url1), fetch(url2)]); 两项申请将同时进行。
一旦你得到了这两个答案,你就可以申请了。 await 将每个对象转换为 JSON: const d1 = await r1.json(); const d2 = await r2.json();最后,这种情况非常普遍。 合并数据 使用传播算子: const datosCombinados = { ...d1, ...d2 };这样就形成了一种独特的结构,融合了两者的特性。
整个块应该被包裹在一个 try..catch因为任何一个请求的失败都会导致 Promise.all() 被拒绝。多亏了 async/await,我们才能处理这类情况。 并发操作 它外观简洁,但性能非常强大。
当你的 Web 应用程序需要……时,这种模式尤其有用。 多个独立资源 在视图渲染之前:例如,同时获取配置、用户数据和使用统计信息,从而减少整体等待时间。
异步/等待相对于“纯粹”的 Promise 和回调的真正优势
萨尔瓦多使用 async/await 提供了许多实际好处 从最早的示例到大规模应用,这些优势都显而易见。最明显的是可读性:流程从上到下编写,无需嵌套或链式函数。 .then() 一个接一个。
另一个优点是 代码可维护性使用更多线性模块,可以更轻松地进行更改、将部分代码提取到辅助函数中或重用逻辑。这直接减少了错误,并使项目新手更容易上手。
错误处理 try..catch 而不是多个 .catch() 疏散 它有助于集中故障逻辑。您可以围绕多个 await 使用单个块并实施一致的错误管理策略,同时不丢失问题发生在哪一行的详细信息。
从性能角度来看,async/await 本身并不能使代码“更快”,但它可以鼓励你使用类似这样的模式: Promise.all() 并行执行任务,而这些任务以前可能是串行执行的。结果是: 总等待时间减少 对于最终用户。
此外,async/await 是 与传统承诺相符您无需重写所有代码即可采用它。您可以合并使用它的函数。 .then() 与其他用户 await 没问题,这样更容易逐步迁移项目。
在现代生态系统中,无论是浏览器还是 Node.js,大多数工具和库都依赖于 Promise 和 async/await。因此,熟练掌握这些概念不再是可选项,而是必选项。 使用现代 JavaScript 的一项要求 在专业项目中。
所有这些概念——承诺、async/await、 fetch, Promise.all尝试/纠正方法和关键术语表为写作构成了一个非常坚实的框架。 清晰、易读且健壮的异步代码掌握这些技术,你就可以构建响应迅速、充分利用网络、且易于长期维护的现代 Web 应用程序。
对字节世界和一般技术充满热情的作家。我喜欢通过写作分享我的知识,这就是我在这个博客中要做的,向您展示有关小工具、软件、硬件、技术趋势等的所有最有趣的事情。我的目标是帮助您以简单而有趣的方式畅游数字世界。