文章中英模式
布魯斯前端JS面試題目 - Promise、Async/Await 深入解析
深入講解 JavaScript 中 Promise、Async/Await 的運作原理、使用方式、常見方法及實作,幫助你輕鬆應對面試中的相關問題。
文章中英模式
懶得看文章?那就來看影片吧
Promise 基本概念
Promise 是 JavaScript 中處理非同步操作的標準方式,代表一個尚未完成但未來會完成的操作。Promise 有三種狀態:
- •
Pending(等待中)
:初始狀態,既不是成功也不是失敗 - •
Fulfilled(已實現)
:操作成功完成 - •
Rejected(已拒絕)
:操作失敗
const promise = new Promise((resolve, reject) => {
// Asynchronous operation
if (/* success */) {
resolve(value); // state changes to fulfilled
} else {
reject(error); // state changes to rejected
}
});
為何需要 Promise?
Promise 解決了傳統回調函式的多個問題,提供了更優雅的非同步處理方式:
- •
避免回調地獄
:解決巢狀回調導致的程式碼難以維護問題 - •
統一錯誤處理
:使用 catch 方法統一處理錯誤 - •
鏈式調用
:通過 then 方法實現清晰的流程控制 - •
更好的語義化
:Promise 的狀態轉換更符合程式設計直覺
// 傳統回調方式
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log(finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
// Promise 方式
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(finalResult))
.catch(failureCallback);
Async/Await 的優勢
Async/Await 是建立在 Promise 之上的語法糖,提供了更直觀的方式來處理非同步操作:
- •
同步風格
:使非同步程式碼看起來像同步程式碼 - •
錯誤處理
:可以使用傳統的 try/catch 處理錯誤 - •
除錯方便
:可以像同步程式碼一樣逐行除錯 - •
條件處理
:更容易實現複雜的條件判斷邏輯
async function fetchUserData() {
try {
const response = await fetch('/api/user');
const userData = await response.json();
return userData;
} catch (error) {
console.error('Error fetching user data:', error);
throw error;
}
}
Promise 的靜態方法
Promise.all()
並行執行多個 Promise,等待全部完成。如果任一 Promise 失敗,整個操作就失敗。
Promise.all([
fetch('/api/users'),
fetch('/api/posts')
]).then(([users, posts]) => {
// process results
});
Promise.race()
返回最先完成的 Promise 結果,無論成功或失敗。
Promise.race([
fetch('/api/data'),
new Promise((_, reject) =>
setTimeout(() => reject('Timeout'), 5000)
)
]);
Promise.any()
返回第一個成功的 Promise 結果,只有全部失敗時才會拒絕。
Promise.any([
fetch('https://api1.example.com'),
fetch('https://api2.example.com')
]).then(firstSuccess => {
// Handle the first successful result
});
Promise.allSettled()
等待所有 Promise 完成,返回每個 Promise 的結果狀態。
Promise.allSettled([
fetch('/api/users'),
fetch('/api/posts')
]).then(results => {
// process all results
});
🔥 常見面試題目
(一)Promise 的狀態轉換規則是什麼?
解答:Promise 的狀態轉換有以下特點:
- •初始狀態為 Pending(等待中)
- •只能從 Pending 轉換到 Fulfilled(已實現)或 Rejected(已拒絕)
- •狀態轉換是不可逆的,一旦改變就不能再變化
- •狀態轉換時會觸發對應的回調函式(then 或 catch)
(二)Promise.all 和 Promise.race 的區別?
解答:
特性 | Promise.all | Promise.race |
---|---|---|
完成條件 | 所有 Promise 都完成 | 任一 Promise 完成 |
失敗處理 | 任一失敗則整體失敗 | 採用最先完成的結果 |
返回值 | 所有結果的陣列 | 最先完成的結果 |
(三)async/await 和 Promise 的關係?
解答:
- •
async/await
是建立在 Promise 之上的語法糖 - •
async
函式總是返回一個 Promise - •
await
只能在 async 函式內使用 - •await 可以等待任何
"thenable"
物件(實現了 then 方法的物件)
// async 函式總是返回 Promise
async function example() {
return "Hello";
}
// 等同於
function example() {
return Promise.resolve("Hello");
}
// 驗證 async 返回的是 Promise
console.log(example()); // Promise {<fulfilled>: "Hello"}
// await 解開的是 Promise resolve 的值
async function example() {
const result = await example(); // 等待 Promise.resolve("Hello") 的結果
console.log(result); // "Hello"
// 等同於
example().then(value => console.log(value)); // "Hello"
}
// 完整範例比較
// Promise 方式
function fetchData() {
return fetch('/api/data')
.then(response => response.json())
.then(data => data)
.catch(error => console.error(error));
}
// async/await 方式 - 更易讀的同步風格
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data; // 自動被包裝為 Promise.resolve(data)
} catch (error) {
console.error(error);
}
}
(四)如何實現自己的 Promise.all?
解答:我們可以實現一個簡單的 MyPromise.all 函數,模擬 Promise.all 的功能:
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
// 如果傳入的不是陣列,直接拒絕
if (!Array.isArray(promises)) {
return reject(new TypeError('promises must be an array'));
}
const results = []; // 存儲所有 promise 的結果
let completed = 0; // 已完成的 promise 數量
// 如果是空陣列,直接返回空陣列結果
if (promises.length === 0) {
return resolve(results);
}
// 遍歷所有 promise
promises.forEach((promise, index) => {
// 使用 Promise.resolve 確保處理非 Promise 值
Promise.resolve(promise)
.then(result => {
results[index] = result; // 保持原始順序
completed++;
// 當所有 promise 都完成時,解析最終結果
if (completed === promises.length) {
resolve(results);
}
})
.catch(error => {
// 任何一個 promise 拒絕,整個 myPromiseAll 就拒絕
reject(error);
});
});
});
}
// 使用範例
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 100));
const p3 = Promise.resolve(3);
myPromiseAll([p1, p2, p3])
.then(results => console.log(results)) // [1, 2, 3]
.catch(error => console.error(error));
這個實現的關鍵點:
- •返回一個新的
Promise
- •追蹤所有
Promise
的完成狀態 - •保持結果的原始順序
- •任一
Promise
失敗時立即拒絕整個Promise
- •使用
Promise.resolve
處理非 Promise 值