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.pswp.cn/news/899804.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899804.shtml
英文地址,請注明出處:http://en.pswp.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 的倍數…