TypeScript 中 await 的详解

TypeScript 中 await 的详解

  • 1. 基本概念
  • 2. 语法要求
  • 3. 工作原理
  • 4. 与 Promise 的比较
  • 5. 实践中的注意事项
  • 总结

本文详细介绍了 TypeScript 中 await 的工作原理、语法要求、与 Promise 的关系以及实践中需要注意的问题,同时针对代码示例进行了优化和补充说明。


1. 基本概念

  • 异步编程背景
    传统异步编程常采用回调函数或 Promise 链式调用,这容易导致“回调地狱”(即指的是在处理异步操作时,由于多个回调函数层层嵌套,导致代码结构混乱、可读性差、难以维护和调试的现象)。async/await 提供了类似同步代码的写法,使逻辑更清晰,便于调试和错误处理。

  • await 的作用

    • await 用于等待一个 Promise 的解析结果。当执行到 await 表达式时,当前的 async 函数暂停执行,直到等待的 Promise 进入 成功(resolve)拒绝(reject) 状态,然后继续执行后续代码。
    • 此外,async 函数会自动捕获同步错误,将其转换为返回 Promise 的拒绝状态。
  • async 函数返回值及状态变化

    • async 函数总是返回一个 Promise:

      • 如果函数返回一个非 Promise 值,会等同于返回 Promise.resolve(值)
      • 如果函数内部抛出异常(无论同步或异步错误),返回的 Promise 则进入拒绝状态(reject),异常作为拒绝原因。
    • 示例:

    async function getNumber() {return 42; // 等同于 return Promise.resolve(42)
    }async function throwError() {throw new Error("失败"); // 返回被拒绝的 Promise,状态为 reject
    }
    

2. 语法要求

  • 使用限制
    await 只能在标记为 async 的函数内部使用,否则会导致语法错误。例如:

    async function getData() {const response = await fetch("https://api.example.com/data");return response.json();
    }
    
  • 对非 Promise 值的处理
    如果 await 后面的表达式不是一个 Promise,执行时会直接返回该值(等同于 await Promise.resolve(值))。例如:

    async function testNonPromise() {const result = await 42; // 直接返回 42,等同于 await Promise.resolve(42)console.log(result); // 输出 42
    }
    testNonPromise();
    
  • 对 thenable 对象的处理
    如果表达式是一个具有 then 方法的对象,则会按照 Promise 的规则处理。


3. 工作原理

  • 暂停与状态机
    当遇到 await 表达式时,当前 async 函数会暂停执行,其内部状态被保存。等待 Promise 解析后,会恢复执行。在编译为 ES5 等低版本目标时,TypeScript 会生成类似生成器函数的状态机代码,通常借助 __awaiter 辅助函数实现。

  • 非阻塞主线程
    尽管 async 函数内部暂停执行,但这不会阻塞 JavaScript 的事件循环,主线程仍可响应其他任务。

  • 错误处理
    错误处理方式有两种:

    • try/catch 捕获
      可集中处理多个 await 操作中的错误,适用于同步与异步错误均可捕获。

      async function fetchData() {try {const response = await fetch("https://api.example.com/data");if (!response.ok) throw new Error("请求失败");const data = await response.json();console.log(data);} catch (error) {console.error("获取数据失败:", error);}
      }
      

      说明:try/catch 块不仅能捕获 await 等待期间的异步错误,还能捕获函数内部抛出的同步错误。

    • 使用 .catch() 方法
      可在单个 Promise 后直接捕获错误,并返回默认值以便后续流程继续。

      async function fetchDataWithCatch() {const response = await fetch("https://api.example.com/data").catch(error => {console.error("获取数据失败:", error);return null;});if (response) {if (!response.ok) throw new Error("请求失败");const data = await response.json();console.log(data);}
      }
      

4. 与 Promise 的比较

  • 可读性提升
    使用 async/await 使得代码逻辑看起来更接近同步流程,避免了大量 .then() 的嵌套,使错误处理更为集中。

  • 编译转换细节
    TypeScript 编译器会将 async/await 转换为基于 Promise 的实现。在目标环境为 ES5 或 ES6 时,转换后的代码可能会借助 __awaiter 辅助函数或生成器函数实现状态机逻辑。例如:

    async function fetchData() {const result = await fetch("https://api.example.com/data");if (!result.ok) throw new Error("请求失败");return await result.json();
    }
    

    转换后相当于:

    function fetchData() {return __awaiter(this, void 0, void 0, function* () {const result = yield fetch("https://api.example.com/data");if (!result.ok) throw new Error("请求失败");return yield result.json();});
    }
    

    说明:这里展示的转换逻辑只是示例,具体实现依赖 TypeScript 版本和目标运行环境。


5. 实践中的注意事项

  • 错误处理策略
    对每个 await 操作都建议采用 try/catch 或在调用处使用 .catch() 来捕获错误,确保程序健壮性。
    例如:

    async function riskyOperation() {const response = await fetch("https://api.example.com/data");if (!response.ok) throw new Error("请求失败");return response.json();
    }riskyOperation().catch(error => {console.error("外部捕获错误:", error);
    });
    
  • 并行与串行操作
    对于多个互不依赖的异步操作,若依次使用 await 会导致串行执行,从而影响性能。建议使用 Promise.all 并行处理:

    async function fetchMultipleData() {const [data1, data2] = await Promise.all([fetch("https://api.example.com/data1").then(res => {if (!res.ok) throw new Error("data1 请求失败");return res.json();}),fetch("https://api.example.com/data2").then(res => {if (!res.ok) throw new Error("data2 请求失败");return res.json();})]);console.log(data1, data2);
    }
    

    若希望即使部分操作失败也能获得全部结果,则可使用 Promise.allSettled

    async function fetchMultipleDataWithAllSettled() {const results = await Promise.allSettled([fetch("https://api.example.com/data1").then(res => res.json()),fetch("https://api.example.com/data2").then(res => res.json())]);results.forEach(result => {if (result.status === 'fulfilled') {console.log(result.value);} else {console.error("失败原因:", result.reason);}});
    }
    

    说明:在 Promise.allSettled 的示例中,result.reason 类型为 any,视情况可进行类型断言处理。

  • 其他实践建议

    • HTTP 状态码检查:建议对 fetch 返回的响应进行 response.ok 检查,以确保请求成功
    • 循环中的 await:当需要对异步迭代(如读取流、异步生成器)时,可使用 for-await-of 循环
    • top-level await:在模块环境下(ESM 模块)TypeScript 也支持顶层 await,但需要确保目标环境的兼容性
    • 术语统一:文中统一使用“拒绝(reject)”描述 Promise 的拒绝状态

总结

通过使用 async/await,我们可以编写出逻辑清晰、易于调试和维护的异步代码。关键要点包括:

  • await 只能在 async 函数内使用;
  • async 函数返回 Promise,内部的同步错误会转换为 Promise 的拒绝状态;
  • 合理使用 try/catch 和 .catch() 进行错误处理;
  • 对于多个互不依赖的异步操作,建议采用并行执行方式(如 Promise.allPromise.allSettled)以提升性能;
  • 需注意 HTTP 响应状态码、循环中的异步处理以及 top-level await 的环境要求。

同时需要认识到,async/await 并非完全替代 Promise,而是对其进行封装和补充,使得异步代码在语义和结构上更加直观。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/899804.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ThreadLocal 深度解析

一、引言 在多线程编程的复杂世界中,数据共享与隔离是一个核心且具有挑战性的问题。ThreadLocal 作为 Java 并发包中的重要工具,为我们提供了一种独特的线程局部变量管理方式,使得每个线程都能拥有自己独立的变量副本,避免了多线…

VMware安装Ubuntu实战分享

在日常开发和学习过程中,很多人都会选择在VMware虚拟机上安装Ubuntu,以便进行Linux环境的体验和开发调试。本文将详细分享在VMware Workstation上安装Ubuntu的全过程,并结合个人经验,提供一些实用的小技巧,帮助大家顺利…

阻止上传可执行程序

点击工具中的文件服务器资源管理器 、然后点击文件屏蔽管理中的文件屏蔽,然后导入目标文件选择要限制的属性即可

微服务面试题:配置中心

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…

系统思考反馈

最近交付的都是一些持续性的项目,越来越感觉到,系统思考和第五项修炼不只是简单的一门课程,它们能真正融入到我们的日常工作和业务中,帮助我们用更清晰的思维方式解决复杂问题,推动团队协作,激发创新。 特…

MMD 转 STL,拓宽 3D 模型应用边界:方法与门道

在 3D 建模与打印领域,不同格式文件间的转换是常见需求。MMD(MikuMikuDance)模型文件格式常用于动漫角色的舞蹈创作等,而 STL(Stereolithography)格式则广泛应用于 3D 打印与计算机辅助设计(CAD…

C语言 【初始指针】【指针一】

引言 思绪很久,还是决定写一写指针,指针这块内容很多,也不是那么容易说清楚,这里尽可能写地详细,让大家理解指针。(未完序) 一、内存和地址 在讲指针前,需要有一个对内存和地址的认…

深入理解pthread多线程编程:从基础到生产者-消费者模型

前言 在多核处理器普及的今天,多线程编程已成为提高程序性能的重要手段。POSIX线程(pthread)是Unix/Linux系统下广泛使用的多线程API。本文将系统介绍pthread的关键概念,包括线程初始化、死锁预防、递归锁使用,并通过…

springboot 对接马来西亚数据源API等多个国家的数据源

使用Spring Boot对接StockTV全球金融数据API指南 StockTV提供了覆盖股票、外汇、期货和加密货币的全球化金融数据接口。本文将通过Spring Boot实现对这些API的快速对接,并提供完整的代码示例。 一、前期准备 1. 获取API Key 访问StockTV官网联系客服获取API Key…

软件测试常用设计模式

设计模式的重要原则就是:高内聚、低耦合;通常程序结构中各模块的内聚程度越高,模块间的耦合程度就越低。 数据驱动测试:Data Driven Testing,简称DDT; 数据驱动指的是从数据文件(如数据库、Ex…

基于 Fluent-Bit 和 Fluentd 的分布式日志采集与处理方案

#作者:任少近 文章目录 需求描述系统目标系统组件Fluent BitFluentdKafka 数据流与处理流程日志采集日志转发到 Fluentd日志处理与转发到 KafkaKafka 作为消息队列 具体配置Fluent-Bit的CM配置Fluent-Bit的DS配置Fluentd的CM配置Fluentd的DS配置Kafka查询结果 需求…

正则表达式(Regular Expression,简称 Regex)

一、5w2h(七问法)分析正则表达式 是的,5W2H 完全可以应用于研究 正则表达式(Regular Expressions)。通过回答 5W2H 的七个问题,我们可以全面理解正则表达式的定义、用途、使用方法、适用场景等&#xff0c…

爬虫获取1688关键字搜索接口的实战指南

在当今电商行业竞争激烈的环境下,数据的重要性不言而喻。1688作为国内领先的B2B电商平台,拥有海量的商品信息,这些数据对于商家的市场分析、选品决策、价格策略制定等都有着重要的价值。本文将详细介绍如何通过爬虫技术获取1688关键字搜索接口…

如何快速解决django存储session变量时出现的django.db.utils.DatabaseError错误

我们在学习django进行web编程的时候,有时需要将一些全局变量信息存储在session中,但使用过程中,却发现会引起数据库的报错。通过查看django源码信息,发现其对session信息进行了ORM映射,如果数据库中不存在对应的表信息…

C语言复习--assert断言

assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断⾔”。 assert(p ! NULL); 代码在程序运⾏到这⼀⾏语句时,验证变量 p 是否等于 NULL 。如果确实不等于 NU…

STL新增内容

文章目录 C11 中的 STL 新增内容容器算法 C14 中的 STL 新增内容容器算法 C17 中的 STL 新增内容容器算法 C20 中的 STL 新增内容容器算法 C11 中的 STL 新增内容 容器 std::array:这是一个固定大小的数组容器,和原生数组类似,但具备更好的…

C#测试Excel开源组件ExcelDataReader

使用微软的com组件Microsoft.office.Interop.Excel读写Excel文件虽然可用,但是列多、行多的时候速度很慢,之前测试过Sylvan.Data.Excel包的用法,如果只是读取Excel文件内容的话,还可以使用ExcelDataReader包,后者是C#开…

位置编码汇总 # 持续更新

看了那么多还没有讲特别好的,GPT老师讲的不错关于三角函数编码。 一、 手撕transformer常用三角位置编码 GPT说:“低维度的编码(例如,第一个维度)可以捕捉到大的位置差异,而高维度的编码则可以捕捉到小的细…

Java 模块系统深度解析

Java 模块系统深度解析 Java 模块系统(Java Platform Module System, JPMS)是 Java 9 引入的一项重要特性,它从根本上改变了 Java 应用程序的打包和依赖管理方式。本文将全面介绍 Java 模块系统的核心概念、优势及实际应用。 一、为什么需要…

蓝桥杯杯赛-日期模拟

知识点 处理日期 1. 按天枚举日期:逐天遍历起始日期到结束日期范围内的每个日期。 2. 处理闰年:正确判断闰年条件。闰年定义为:年份 满足以下任意一个条件:(闰年的2月只有29天) 满足下面一个条件就是闰年 1> 是 400 的倍数…