一、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 的區別
特性 | Promise | async/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合理使用并行操作