当前位置: 首页 > news >正文

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,而是对其进行封装和补充,使得异步代码在语义和结构上更加直观。

相关文章:

  • 通用目标检测技术选型分析报告--截止2025年4月
  • 从零构建大语言模型全栈开发指南:第四部分:工程实践与部署-4.2.2多模态数据处理:图像编码与文本对齐(实战代码示例)
  • OpenAI即将开源!DeepSeek“逼宫”下,AI争夺战将走向何方?
  • 人工智能基础知识笔记六:方差分析
  • CUDA安装步骤注意事项
  • SecureCRT常用命令
  • 图生生AI生图,图片风格模仿,复刻爆款风格
  • pytorch模型的进阶训练和性能优化
  • Java事务管理:编程式事务 vs 声明式事务
  • GIT 撤销上次推送
  • ai图片视频生成wan模型
  • 【Easylive】convertLine2Tree 方法详解
  • Keil5中的C/C++选项下的GUN extensions什么意思?
  • 微前端知识内容
  • [Kerberos] 简化的加密和校验和总则
  • MYSQL8.0以上版本 主从复制
  • C++11QT复习 (十)
  • 中科驭数受邀参展2025中关村论坛 DPU受主流媒体关注
  • 从 Java 到 Go:面向对象的巨人与云原生的轻骑兵
  • [250331] Paozhu 发布 1.9.0:C++ Web 框架,比肩脚本语言 | DeaDBeeF 播放器发布 1.10.0
  • 共建医学人工智能高地,上海卫健委与徐汇区将在这些方面合作
  • 奥运“四朝元老”华天回国参赛,伤势未愈谨慎出战全国锦标赛
  • 第一集|好饭不怕晚,折腰若如初见
  • 现场丨在胡适施蛰存等手札与文献间,再读百年光华
  • 4台肺癌手术,2名“90后”患者,这届年轻人的肺怎么了?
  • 微软宣布将裁员3%