JavaScript?async/await的使用場景與規(guī)范
JavaScript異步編程指南:async/await的使用場景與規(guī)范
一、async/await是什么?為什么它是異步編程的救星?
1. 核心概念
async和await是ES2017推出的異步編程語法糖,本質(zhì)是Promise的語法簡化版:
async修飾的函數(shù)會隱式返回Promise,相當于自動給返回值套上Promise.resolve()await只能在async函數(shù)內(nèi)使用,能讓代碼"暫停"等待Promise結果,使異步代碼擁有同步寫法的直觀性
2. 對比傳統(tǒng)異步方案
回調(diào)函數(shù):
fs.readFile('data.txt', (err, data) => {
if (err) throw err;
console.log(data);
});
Promise鏈式調(diào)用:
fetch('api/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
async/await:
async function getData() {
try {
const res = await fetch('api/data');
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
}
優(yōu)勢:代碼結構更接近同步邏輯,避免回調(diào)地獄和Promise多層嵌套,錯誤處理更統(tǒng)一。
二、核心用法詳解
1. async函數(shù)基礎語法
// 定義async函數(shù),返回Promise
async function getUser() {
return { id: 1, name: "張三" };
}
// 等價于
function getUser() {
return Promise.resolve({ id: 1, name: "張三" });
}
// 調(diào)用方式
getUser().then(user => {
console.log(user.name); // 輸出:張三
});
2. await關鍵字的正確姿勢
async function showDelayMessage() {
// 封裝延遲Promise
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
console.log("開始執(zhí)行");
await delay(2000); // 代碼在此暫停2秒
console.log("2秒后執(zhí)行");
const message = await new Promise(resolve => {
resolve("延遲消息");
});
console.log(message); // 輸出:延遲消息
}
showDelayMessage();
3. 錯誤處理最佳實踐
async function fetchData(url) {
try {
// 等待請求響應
const response = await fetch(url);
// 檢查響應狀態(tài)
if (!response.ok) {
throw new Error(`請求失敗: ${response.status}`);
}
// 等待數(shù)據(jù)解析
const data = await response.json();
return data;
} catch (error) {
console.error("數(shù)據(jù)獲取失敗:", error);
throw error; // 可選:向上拋出錯誤
}
}
三、六大典型使用場景與實戰(zhàn)案例
1. 場景一:網(wǎng)絡請求(最常用場景)
需求:獲取用戶信息后再獲取該用戶的帖子
async function getUserAndPosts(userId) {
try {
// 順序執(zhí)行異步操作
const userResponse = await fetch(`/api/users/${userId}`);
const user = await userResponse.json();
const postsResponse = await fetch(`/api/posts?userId=${user.id}`);
const posts = await postsResponse.json();
return { user, posts };
} catch (error) {
console.error("請求失敗:", error);
}
}
2. 場景二:并行請求優(yōu)化
需求:同時獲取用戶、帖子、評論數(shù)據(jù)
async function fetchAllData() {
try {
// 并行發(fā)起多個請求
const userPromise = fetch("/api/user/1").then(res => res.json());
const postsPromise = fetch("/api/posts").then(res => res.json());
const commentsPromise = fetch("/api/comments").then(res => res.json());
// 等待所有請求完成
const [user, posts, comments] = await Promise.all([
userPromise,
postsPromise,
commentsPromise
]);
return { user, posts, comments };
} catch (error) {
console.error("任一請求失敗:", error);
}
}
3. 場景三:定時器延遲控制
需求:實現(xiàn)一個可復用的延遲函數(shù)
// 封裝延遲Promise
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function taskWithDelay() {
console.log("任務開始");
// 等待3秒
await delay(3000);
console.log("3秒后執(zhí)行");
// 再等待1秒
await delay(1000);
console.log("4秒后執(zhí)行");
}
4. 場景四:文件系統(tǒng)操作(Node.js)
需求:讀取配置文件并解析
const fs = require('fs').promises; // Node.js內(nèi)置Promise化API
async function loadConfig() {
try {
// 讀取文件內(nèi)容
const content = await fs.readFile('./config.json', 'utf-8');
// 解析JSON
const config = JSON.parse(content);
return config;
} catch (error) {
console.error("配置加載失敗:", error);
throw new Error("配置文件異常,請檢查路徑");
}
}
5. 場景五:表單提交與驗證
需求:前端表單提交前驗證并發(fā)送請求
async function handleFormSubmit(formData) {
// 前端驗證
if (!formData.username || !formData.password) {
throw new Error("用戶名和密碼不能為空");
}
try {
// 發(fā)送提交請求
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(formData),
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
if (result.success) {
return "登錄成功";
} else {
throw new Error(result.message || "登錄失敗");
}
} catch (error) {
console.error("提交失敗:", error);
throw error;
}
}
6. 場景六:處理流數(shù)據(jù)(Node.js)
需求:讀取大文件并逐行處理
const fs = require('fs');
const readline = require('readline');
async function processLargeFile(filePath) {
try {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
let lineCount = 0;
// 逐行處理
for await (const line of rl) {
lineCount++;
// 處理每行數(shù)據(jù)
console.log(`行${lineCount}: ${line}`);
}
console.log(`處理完成,共${lineCount}行`);
} catch (error) {
console.error("文件處理失敗:", error);
}
}
四、企業(yè)級代碼規(guī)范與最佳實踐
1. 命名與定義規(guī)范
- 函數(shù)命名:async函數(shù)建議添加
Async后綴(可選),如fetchUserAsync() - 定義方式:優(yōu)先使用普通函數(shù)定義,避免箭頭函數(shù)(可讀性更好)
// 推薦 async function loadData() {} // 不推薦 const loadData = async () => {} - 變量命名:await后的變量名建議體現(xiàn)異步操作含義,如
await fetchResponse
2. 錯誤處理規(guī)范
- 必用try…catch:所有await操作必須包裹在try塊中,catch統(tǒng)一處理錯誤
async function main() { try { const data = await fetchData(); await processData(data); } catch (error) { console.error("全局錯誤捕獲:", error); // 記錄錯誤日志(如上報到監(jiān)控系統(tǒng)) sendErrorToMonitor(error); } } - 錯誤分類處理:根據(jù)錯誤類型做不同處理(示例)
async function handleError() { try { // ... } catch (error) { if (error instanceof NetworkError) { console.log("網(wǎng)絡錯誤,重試中..."); // 重試邏輯 } else if (error instanceof AuthError) { console.log("認證失敗,跳轉登錄頁"); // 跳轉邏輯 } else { console.error("未知錯誤:", error); } } }
3. 性能優(yōu)化規(guī)范
- 并行任務用Promise.all:避免順序等待浪費時間
// 差:順序執(zhí)行(總耗時=3s+2s=5s) async function bad() { await delay(3000); await delay(2000); } // 好:并行執(zhí)行(總耗時=3s) async function good() { await Promise.all([delay(3000), delay(2000)]); } - 限制并發(fā)數(shù)量:處理大量并行請求時控制并發(fā)數(shù)(示例)
async function fetchWithConcurrency(urls, maxConcurrent) { const results = []; const fetchQueue = urls.map(url => { return async () => { try { const res = await fetch(url); results.push(await res.json()); } catch (err) { results.push(err); } }; }); // 分批執(zhí)行 for (let i = 0; i < fetchQueue.length; i += maxConcurrent) { const batch = fetchQueue.slice(i, i + maxConcurrent); await Promise.all(batch.map(fetchFn => fetchFn())); } return results; }
4. 代碼風格規(guī)范
- 一行一await:每個await單獨一行,提高可讀性
// 推薦 const response = await fetch(url); const data = await response.json(); // 不推薦 const data = await await fetch(url).then(res => res.json());
- 合理使用return:及時return避免不必要的等待
async function getData() { if (cacheAvailable) { return getFromCache(); // 直接返回緩存數(shù)據(jù),無需等待 } const data = await fetchFromServer(); return data; }
5. 特殊場景處理
- 處理超時:為await操作添加超時控制
// 封裝超時Promise function timeout(ms) { return new Promise((_, reject) => { setTimeout(() => reject(new Error("操作超時")), ms); }); } async function fetchWithTimeout(url, timeoutMs) { try { return await Promise.race([ fetch(url), timeout(timeoutMs) ]); } catch (error) { console.error("請求超時:", error); throw error; } } - 處理取消操作:使用AbortController取消異步請求
async function fetchWithAbort(url) { const controller = new AbortController(); const signal = controller.signal; // 5秒后取消請求 setTimeout(() => { controller.abort(); console.log("請求已取消"); }, 5000); try { const response = await fetch(url, { signal }); return await response.json(); } catch (error) { if (error.name === "AbortError") { console.log("請求被取消"); } else { console.error("請求失敗:", error); } } }
五、瀏覽器兼容性與解決方案
1. 主流瀏覽器支持情況
| 瀏覽器 | 最低支持版本 | 支持時間 |
|---|---|---|
| Chrome | 55+ | 2016年12月 |
| Firefox | 52+ | 2017年3月 |
| Safari | 11+ | 2017年9月 |
| Edge | 15+ | 2017年4月 |
| Opera | 42+ | 2016年12月 |
2. 兼容性解決方案
方案一:使用babel轉譯
- 安裝依賴:
npm install @babel/core @babel/preset-env @babel/cli -D
- 創(chuàng)建
.babelrc配置文件:{ "presets": [ ["@babel/preset-env", { "targets": { "browsers": ["last 2 versions", "ie >= 11"] } }] ] } - 轉譯命令:
npx babel src -d dist
方案二:引入polyfill
- 安裝
regenerator-runtime:npm install regenerator-runtime
- 在代碼中引入:
import "regenerator-runtime/runtime";
六、進階技巧與面試高頻問題
1. 如何處理Promise.all中的部分失???
async function fetchWithPartialFail(urls) {
const results = [];
for (const url of urls) {
try {
const res = await fetch(url);
results.push(await res.json());
} catch (error) {
results.push({ error: true, message: error.message });
console.log(`請求${url}失敗`, error);
}
}
return results;
}
2. await能在普通函數(shù)中使用嗎?為什么?
不能。因為await必須在async函數(shù)內(nèi)使用,本質(zhì)上async函數(shù)會被編譯為生成器函數(shù)(Generator),而await是生成器中yield的語法糖,普通函數(shù)無法使用該特性。
3. async/await和Promise的本質(zhì)區(qū)別?
async/await是語法糖,底層仍基于Promise實現(xiàn)async/await讓異步代碼擁有同步的寫法,更符合人類思維習慣async/await通過try...catch統(tǒng)一處理錯誤,比Promise的.catch()更直觀
七、總結:async/await使用三原則
- 能用async/await就不用傳統(tǒng)Promise:除非需要處理極其復雜的異步流程控制
- 每個await必配try…catch:避免未捕獲的異步錯誤導致程序崩潰
- 合理選擇并行/順序執(zhí)行:IO密集型任務用Promise.all并行處理,計算密集型任務考慮Web Worker
掌握async/await不僅能寫出更優(yōu)雅的異步代碼,還能提升效率。
到此這篇關于JavaScript async/await的使用場景與規(guī)范的文章就介紹到這了,更多相關JS async/await指南內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JS/HTML5游戲常用算法之路徑搜索算法 A*尋路算法完整實例
這篇文章主要介紹了JS/HTML5游戲常用算法之路徑搜索算法 A*尋路算法,結合完整實例形式分析了A*尋路算法的具體實現(xiàn)技巧,代碼備有詳盡的注釋便于理解,需要的朋友可以參考下2018-12-12
Typescript高級類型Record,Partial,Readonly詳解
這篇文章主要介紹了Typescript高級類型Record,Partial,Readonly等介紹,keyof將一個類型的屬性名全部提取出來當做聯(lián)合類型,本文通過實例代碼給大家詳細講解需要的朋友可以參考下2022-11-11
javascript實現(xiàn)的LI列表輸出,隔行同色的代碼
javascript實現(xiàn)的LI列表輸出,隔行同色的代碼...2007-10-10

