JavaScript 異步編程:Promise 與 async/await 詳解

一、Promise

1. 什么是 Promise?

Promise 是 JavaScript 中用于處理異步操作的對象,它代表一個異步操作的最終完成(或失敗)及其結果值。

2. Promise 的三種狀態
??Pending(待定)??:初始狀態,既不是成功,也不是失敗狀態
??Fulfilled(已兌現)??:意味著操作成功完成
??Rejected(已拒絕)??:意味著操作失敗
3. 創建 Promise
const myPromise = new Promise((resolve, reject) => {// 異步操作if (/* 操作成功 */) {resolve(value); // 將Promise狀態改為fulfilled} else {reject(error);  // 將Promise狀態改為rejected}
});
4. Promise 的方法

then()

myPromise.then((value) => { /* 成功時的處理 */ },(error) => { /* 失敗時的處理 */ }
);

catch()

myPromise.catch((error) => { /* 失敗時的處理 */ }
);

finally()

myPromise.finally(() => { /* 無論成功失敗都會執行 */ }
);
5. Promise 的靜態方法

Promise.resolve()

Promise.resolve(value); // 返回一個已解決的Promise

Promise.reject()

Promise.reject(reason); // 返回一個已拒絕的Promise

Promise.all()

Promise.all([promise1, promise2, ...]).then((values) => { /* 所有Promise都成功 */ }).catch((error) => { /* 任一Promise失敗 */ });

Promise.race()

Promise.race([promise1, promise2, ...]).then((value) => { /* 第一個完成的Promise */ });

Promise.allSettled() (ES2020)

Promise.allSettled([promise1, promise2, ...]).then((results) => { /* 所有Promise都完成 */ });
6. Promise 鏈式調用
doSomething().then(result => doSomethingElse(result)).then(newResult => doThirdThing(newResult)).then(finalResult => console.log(`Got the final result: ${finalResult}`)).catch(failureCallback);

二、async/await

1. 什么是 async/await?

async/await 是建立在 Promise 之上的語法糖,讓異步代碼看起來更像同步代碼。

2. async 函數
async function myFunction() {return "Hello";
}

// 等同于

function myFunction() {return Promise.resolve("Hello");
}
3. await 表達式
async function myFunction() {const result = await somePromise;console.log(result);
}
4. 錯誤處理

try/catch

async function myFunction() {try {const result = await somePromise;console.log(result);} catch (error) {console.error(error);}
}

結合 catch()

async function myFunction() {const result = await somePromise.catch(error => {console.error(error);return defaultValue;});console.log(result);
}
5. 并行執行
async function myFunction() {// 順序執行const result1 = await promise1;const result2 = await promise2;// 并行執行const [result1, result2] = await Promise.all([promise1, promise2]);
}
6. async/await 與 Promise 的對比
// 使用Promise
function getJSON() {return fetch('/data.json').then(response => {if (response.status === 200) {return response.json();} else {throw new Error('Unable to fetch data');}}).then(data => console.log(data)).catch(err => console.log(err));
}// 使用async/await
async function getJSON() {try {const response = await fetch('/data.json');if (response.status === 200) {const data = await response.json();console.log(data);} else {throw new Error('Unable to fetch data');}} catch (err) {console.log(err);}
}

三、最佳實踐

1. Promise 最佳實踐
??總是返回或終止 Promise 鏈??:在 then() 中總是返回新的 Promise 或 throw 錯誤
??避免 Promise 嵌套??:使用鏈式調用代替嵌套
??使用 catch() 處理錯誤??:不要忽略 Promise 的錯誤
??命名 Promise 函數??:提高代碼可讀性
2. async/await 最佳實踐
??盡量使用 try/catch??:正確處理錯誤
??避免不必要的 await??:可以并行執行的不要順序執行
??考慮頂層 await??:在模塊頂層使用 await (ES2022)
??不要混用 then() 和 async/await??:選擇一種風格并保持一致

四、高級用法

1. Promise 取消
function cancellablePromise(executor) {let cancel;const promise = new Promise((resolve, reject) => {executor(resolve, reject);cancel = reject;});promise.cancel = cancel;return promise;
}const p = cancellablePromise((resolve, reject) => {setTimeout(() => resolve('Done'), 2000);
});p.cancel('Operation cancelled');
2. async 生成器
async function* asyncGenerator() {let i = 0;while (i < 3) {await new Promise(resolve => setTimeout(resolve, 1000));yield i++;}
}(async function() {for await (const num of asyncGenerator()) {console.log(num);}
})();
3. 頂層 await (ES2022)
// 在模塊頂層直接使用await
const data = await fetch('/data.json').then(res => res.json());
console.log(data);

五、async 函數與 Promise 的關系

1. async 函數的返回值

??不需要??在 async 函數中顯式返回 new Promise,因為 async 函數??自動??將返回值包裝為 Promise:

async function foo() {return 42; // 自動包裝為 Promise.resolve(42)
}

// 等同于

function foo() {return Promise.resolve(42);
}
2. 何時需要顯式使用 new Promise

在以下情況下可能需要顯式使用 new Promise:

??包裝回調式 API??:如文件系統、網絡操作、子進程、定時器、事件監聽、Web API等
async function readFileAsync(path) {return new Promise((resolve, reject) => {fs.readFile(path, (err, data) => {if (err) reject(err);else resolve(data);});});
}

??需要手動控制 resolve/reject 時機??:

async function waitForEvent(element, eventName) {return new Promise((resolve) => {element.addEventListener(eventName, resolve, { once: true });});
}

六、async/await 與 Promise 的區別

特性Promiseasync/await
語法??鏈式調用 .then().catch()類似同步代碼的語法
錯誤處理??使用 .catch() 或第二個參數使用 try/catch 塊
??可讀性??回調嵌套可能導致"回調地獄"線性執行,代碼更清晰
調試??調試較困難(匿名函數)調試更直觀(有函數名和行號)
返回值??總是返回 Promise總是返回 Promise
執行流程??基于事件循環的微任務基于事件循環的微任務

七、如何配合使用 async/await 和 Promise

1. 最佳實踐:混合使用場景

(1) 并行操作 + await

async function fetchAllData() {// 并行啟動所有請求const [users, products, orders] = await Promise.all([fetch('/api/users'),fetch('/api/products'),fetch('/api/orders')]);// 順序處理結果const userData = await users.json();const productData = await products.json();const orderData = await orders.json();return { userData, productData, orderData };
}

(2) 需要精細控制 Promise 時

async function withTimeout(promise, ms) {return new Promise((resolve, reject) => {const timeoutId = setTimeout(() => {reject(new Error('Operation timed out'));}, ms);promise.then(result => {clearTimeout(timeoutId);resolve(result);},error => {clearTimeout(timeoutId);reject(error);});});
}// 使用
try {const result = await withTimeout(fetch('/api/data'), 5000);
} catch (error) {console.error('Error:', error);
}
2. 錯誤處理模式對比

Promise 風格

function getUser(id) {return fetch(`/api/users/${id}`).then(response => {if (!response.ok) throw new Error('Not found');return response.json();}).catch(error => {console.error('Fetch failed:', error);throw error; // 繼續傳遞錯誤});
}

async/await 風格

async function getUser(id) {try {const response = await fetch(`/api/users/${id}`);if (!response.ok) throw new Error('Not found');return await response.json();} catch (error) {console.error('Fetch failed:', error);throw error; // 繼續傳遞錯誤}
}
3. 常見配合使用模式

(1) 異步初始化模式

let initialized = false;
let initializationPromise;async function initialize() {if (initialized) return;if (!initializationPromise) {initializationPromise = (async () => {await loadConfig();await connectToDB();initialized = true;})();}return initializationPromise;
}

// 使用
await initialize();
// 現在可以使用初始化后的資源

(2) 帶緩存的異步請求

const cache = new Map();async function getWithCache(url) {if (cache.has(url)) {return cache.get(url);}const promise = fetch(url).then(response => response.json()).then(data => {cache.set(url, data);return data;});cache.set(url, promise);return promise;
}// 使用
const data = await getWithCache('/api/products');
八、性能考量
??微任務開銷??:await 會創建額外的微任務在熱代碼路徑中避免不必要的 await??內存使用??:每個 await 都會保留執行上下文長異步鏈可能增加內存壓力??優化技巧??:// 次優:創建兩個微任務
async function foo() {return await bar();
}
// 優化:只創建一個微任務
async function foo() {return bar();
}

九、常見問題與解決方案

1. Promise 常見問題

問題:忘記返回 Promise

// 錯誤
somePromise.then(value => {doSomething(value);
});// 正確
somePromise.then(value => {return doSomething(value);
});

問題:忽略錯誤處理

// 錯誤
somePromise.then(value => console.log(value));// 正確
somePromise.then(value => console.log(value)).catch(error => console.error(error));
2. async/await 常見問題

問題:忘記 await

// 錯誤
async function foo() {const result = someAsyncFunction(); // 忘記awaitconsole.log(result); // 輸出Promise對象
}// 正確
async function foo() {const result = await someAsyncFunction();console.log(result);
}

問題:在循環中錯誤使用 await

// 低效 - 順序執行
for (const url of urls) {const response = await fetch(url);console.log(await response.json());
}// 高效 - 并行執行
const promises = urls.map(url => fetch(url).then(res => res.json()));
const results = await Promise.all(promises);
results.forEach(result => console.log(result));

為什么我的 async 函數返回 undefined?

async function foo() {// 忘記 returnPromise.resolve(42);
}
const result = await foo(); // undefined??解決方案??:確保返回你想要的值(Promise 會自動包裝)async function foo() {return Promise.resolve(42); // 顯式返回// 或者直接 return 42;
}

什么時候應該使用 return await?

async function foo() {try {return await riskyOperation(); // 正確:可以捕獲錯誤} catch (error) {return fallbackValue;}
}async function bar() {return riskyOperation(); // 錯誤:無法捕獲錯誤
}

??規則??:

在 try/catch 塊內使用 return await 以捕獲錯誤
否則直接 return Promise 更高效(少一次微任務)

如何取消 async 函數?

function createCancellableAsyncTask(task) {let cancel;const promise = new Promise((resolve, reject) => {cancel = reject;task().then(resolve, reject);});return { promise, cancel };
}// 使用
const { promise, cancel } = createCancellableAsyncTask(async () => {await longRunningOperation();
});
// 取消
cancel(new Error('User cancelled'));

十、總結

Promise 和 async/await 都是 JavaScript 中處理異步操作的重要工具:

??Promise?? 提供了更結構化的異步編程方式,避免了回調地獄
??async/await?? 讓異步代碼看起來更像同步代碼,提高了可讀性
兩者可以結合使用,async 函數總是返回 Promise,await 后面可以跟任何 thenable 對象
在實際開發中,應根據場景選擇合適的方式,并遵循最佳實踐

JavaScript 異步編程:async/await 與 Promise 的關系與配合使用

??async/await 是 Promise 的語法糖??,底層仍然是 Promise
??不需要??在 async 函數中顯式返回 new Promise,除非:包裝回調式 API需要精細控制 resolve/reject 時機
??配合使用原則??:使用 async/await 處理線性異步邏輯使用 Promise 方法(如 Promise.all)處理并行操作在需要精細控制或包裝舊代碼時使用 new Promise
??錯誤處理??:try/catch 用于 async/await.catch() 用于 Promise 鏈
??性能優化??:避免不必要的 await合理使用并行操作

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917108.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917108.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917108.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OS架構整理

OS架構整理引導啟動部分bios bootloader區別啟動流程&#xff08;x86 BIOS 啟動&#xff09;&#xff1a;biosboot_loader3.切換進保護模式實模式的限制如何切換進保護模式加載kernel到內存地址1M加載內核映像文件elf一些基礎知識鏈接腳本與代碼數據段創建GDT表段頁式內存管理顯…

【WRF-Chem第二期】WRF-Chem有關 namelist 詳解

目錄namelist 選項&#xff1a;chem_opt 的選擇其他化學相關的 namelist 選項氣溶膠光學屬性與輸出邊界與初始條件配置&#xff08;氣體&#xff09;參考本博客詳細介紹 WRF-Chem有關 namelist 選項。 namelist 選項&#xff1a;chem_opt 的選擇 chem_opt 是什么&#xff1f;…

STM32-USART串口實現接收數據三種方法(1.根據\r\n標志符、2.空閑幀中斷、3.根據定時器輔助接收)

本章概述思維導圖&#xff1a;USART串口初始化配置串口初始化配置在&#xff08;STM32-USART串口初始化章節有詳細教程配置&#xff09;&#xff0c;本章不做講解直接代碼示例&#xff0c;本章重點在于串口實現接收數據三種方法&#xff1b;配置USART1串口接收初始化函數步驟&a…

【NLP輿情分析】基于python微博輿情分析可視化系統(flask+pandas+echarts) 視頻教程 - 微博評論數據可視化分析-點贊區間折線圖實現

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;最近寫了一套【NLP輿情分析】基于python微博輿情分析可視化系統(flaskpandasecharts)視頻教程&#xff0c;持續更新中&#xff0c;計劃月底更新完&#xff0c;感謝支持。今天講解微博評論數據可視化分析-點贊區間折線圖實現…

Unity_SRP Batcher

SRP Batcher 全面解析&#xff1a;原理、啟用、優化與調試一、什么是 SRP Batcher&#xff1f;SRP Batcher 是 Unity Scriptable Render Pipeline&#xff08;URP、HDRP 或自定義 SRP&#xff09; 專屬的 CPU 渲染性能優化技術&#xff0c;核心目標是 減少材質切換時的 CPU 開銷…

詳解Vite 配置中的代理功能

在前端開發過程中&#xff0c;你可能經常會遇到一個頭疼的問題&#xff1a;當你在本地啟動的前端項目中調用后端接口時&#xff0c;瀏覽器控制臺會報出類似 “Access to fetch at ‘http://xxx’ from origin ‘http://localhost:3000’ has been blocked by CORS policy” 的錯…

理解梯度在神經網絡中的應用

梯度&#xff08;Gradient&#xff09;是微積分中的一個重要概念&#xff0c;廣泛應用于機器學習和深度學習中&#xff0c;尤其是在神經網絡的訓練過程中。下面將從梯度的基本概念、其在神經網絡中的應用兩個方面進行詳細介紹。一、梯度的基本概念 1.1 什么是梯度&#xff1f; …

WPF,按鈕透明背景實現MouseEnter

在幫手程序&#xff08;assister.exe&#xff09;中&#xff0c;可以點擊錄制按鈕&#xff0c;實現錄制用戶操作直接生成操作列表。而在彈出錄制按鈕的懸浮窗中&#xff0c;需要能夠拖動錄制按鈕放置在任意的位置&#xff0c;以免阻擋正常的窗口。具體功能是&#xff0c;當鼠標…

【抄襲】思科交換機DAI(動態ARP監控)配置測試

一.概述 1.DAI作用 ①.使用DAI&#xff0c;管理員可以指定交換機的端口為信任和非信任端口&#xff1a; 信任端口可以轉發任何ARP信息 非信任端口的ARP消息要進行ARP檢測驗證 ②.交換機執行如下的ARP驗證&#xff1a; 靜態ARP監控&#xff1a;為一個靜態的IP地址配置一個靜態AR…

在嵌入式系統或 STM32 平臺中常見的外設芯片和接口

在嵌入式系統或 STM32 平臺中常見的 外設芯片 或 模塊名稱&#xff0c;包括&#xff1a; &#x1f4fa; 顯示驅動&#xff08;如 ST7735、OTM8009A、NT35510&#xff09;&#x1f4f7; 攝像頭模組&#xff08;如 OV5640、OV9655、S5K5CAG&#xff09;&#x1f4be; Flash 存儲器…

AI 類型的 IDE

指集成了 AI 輔助編程能力的集成開發環境 一、代碼輔助生成 ? 自動補全&#xff08;更智能&#xff09; 比傳統 IDE 更智能&#xff0c;理解上下文&#xff0c;生成整個函數/模塊 示例&#xff1a;根據函數名 calculateTax 自動生成稅務計算邏輯 ? 函數 / 類自動生成 給…

JP3-3-MyClub后臺后端(一)

Java道經 - 項目 - MyClub - 后臺后端&#xff08;一&#xff09; 傳送門&#xff1a;JP3-1-MyClub項目簡介 傳送門&#xff1a;JP3-2-MyClub公共服務 傳送門&#xff1a;JP3-3-MyClub后臺后端&#xff08;一&#xff09; 傳送門&#xff1a;JP3-3-MyClub后臺后端&#xff08;…

架構實戰——互聯網架構模板(“存儲層”技術)

目錄 一、SQL 二、NoSQL 三、小文件存儲 四、大文件存儲 本文來源:極客時間vip課程筆記 一、SQL SQL 即我們通常所說的關系數據。前幾年 NoSQL 火了一陣子,很多人都理解為 NoSQL 是完全拋棄關系數據,全部采用非關系型數據。但經過幾年的試驗后,大家發現關系數據不可能完全被…

CentOS7.9在線部署Dify

一、CentOS7.9安裝dify 二、檢查是否安裝dcoker docker --version2.1下載后將安裝包上傳至服務器對應文件夾下,我選在放在了 /root文件夾下 cd /root2.2 上傳至服務器 cd /root #對應目錄下tar -xvf docker-26.1.4.tgz # 解壓安裝包:chmod 755 -R docker # 賦予可執…

深入淺出C語言指針:從數組到函數指針的進階之路(中)

指針是C語言的靈魂&#xff0c;也是初學者最頭疼的知識點。它像一把鋒利的刀&#xff0c;用得好能大幅提升代碼效率&#xff0c;用不好則會讓程序漏洞百出。今天這篇文章&#xff0c;我們從數組與指針的基礎關系講起&#xff0c;一步步揭開指針進階類型的神秘面紗&#xff0c;最…

java web Cookie處理

java web 設置cookie更改啟動端口// Directory tree (5 levels) ├── src\ │ ├── a.txt │ └── com\ │ └── zhang\ │ └── ServletContext\ │ ├── cookie\ │ └── servletContext.java └── web\├─…

機器學習—線性回歸

一線性回歸線性回歸是利用數理統計中回歸分析&#xff0c;來確定兩種或兩種以上變量間相互依賴的定量關系的一種統計分析方法。相關關系&#xff1a;包含因果關系和平行關系因果關系&#xff1a;回歸分析【原因引起結果&#xff0c;需要明確自變量和因變量】平行關系&#xff1…

Spring Boot Admin 監控模塊筆記-實現全鏈路追蹤

一、概述Spring Boot Admin&#xff08;SBA&#xff09;是一個用于監控和管理 Spring Boot 應用程序的工具。它提供了一個 Web 界面&#xff0c;可以集中管理多個 Spring Boot 應用程序的健康狀態、指標、日志、配置等信息。通過 SBA&#xff0c;你可以輕松地監控和管理你的微服…

容器化與Docker核心原理

目錄 專欄介紹 作者與平臺 您將學到什么&#xff1f; 學習特色 容器化與Docker核心原理 引言&#xff1a;為什么容器化成為云計算時代的基石&#xff1f; 容器化技術全景與Docker核心原理&#xff1a;從概念到實踐 文章摘要 1. 引言&#xff1a;為什么容器化成為云計算…

掌握Python三大語句:順序、條件與循環

PS不好意思各位&#xff0c;由于最近筆者在參加全國大學生電子設計大賽&#xff0c;所以最近會出現停更的情況&#xff0c;望大家諒解&#xff0c;比賽結束后我會加大力度&#xff0c;火速講Python的知識給大家寫完&#x1f396;?&#x1f396;?&#x1f396;?&#x1f396;…