promise.all 和 promise.allSettled 的區(qū)別
Promise.all 與 Promise.allSettled 的區(qū)別詳解
這兩個方法都是用于處理多個Promise的并發(fā)執(zhí)行,但在行為和結(jié)果處理上有重要區(qū)別。
?? 核心區(qū)別對比表
| 特性 | Promise.all | Promise.allSettled |
|---|---|---|
| 失敗處理 | 一個失敗立即拒絕整個Promise | 等待所有Promise完成,無論成功失敗 |
| 返回值 | 成功值的數(shù)組 | 對象數(shù)組,包含狀態(tài)和值/原因 |
| 適用場景 | 需要所有操作都成功的場景 | 需要知道每個操作最終結(jié)果的場景 |
| 錯誤處理 | 只能捕獲第一個錯誤 | 可以獲取每個Promise的詳細(xì)結(jié)果 |
?? 詳細(xì)解析
Promise.all - "全有或全無"
// 基本語法
Promise.all([promise1, promise2, promise3, ...])
.then(values => {
// 所有Promise都成功時執(zhí)行
})
.catch(error => {
// 任何一個Promise失敗時立即執(zhí)行
});
特點:
- 如果所有Promise都成功,返回成功值的數(shù)組
- 如果任何一個Promise失敗,立即拒絕,并返回第一個錯誤
- 適用于相互依賴的操作
示例:
const p1 = Promise.resolve('成功1');
const p2 = Promise.resolve('成功2');
const p3 = Promise.reject('失敗!');
const p4 = Promise.resolve('成功4');
// 所有成功的情況
Promise.all([p1, p2, p4])
.then(results => {
console.log(results); // ['成功1', '成功2', '成功4']
})
.catch(error => {
console.log('這里不會執(zhí)行');
});
// 有失敗的情況
Promise.all([p1, p2, p3, p4])
.then(results => {
console.log('這里不會執(zhí)行');
})
.catch(error => {
console.log(error); // '失??!' - 立即捕獲第一個錯誤
// p4的結(jié)果被忽略,即使它成功了
});
Promise.allSettled - "全部見分曉"
// 基本語法
Promise.allSettled([promise1, promise2, promise3, ...])
.then(results => {
// 所有Promise都完成時執(zhí)行(無論成功失?。? results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失敗:', result.reason);
}
});
});
特點:
- 等待所有Promise完成(成功或失敗)
- 返回對象數(shù)組,每個對象包含狀態(tài)和值/原因
- 永遠(yuǎn)不會被拒絕
- 適用于需要知道每個操作結(jié)果的場景
示例:
const p1 = Promise.resolve('成功1');
const p2 = Promise.resolve('成功2');
const p3 = Promise.reject('失敗3');
const p4 = Promise.reject('失敗4');
Promise.allSettled([p1, p2, p3, p4])
.then(results => {
console.log(results);
// 輸出:
// [
// { status: 'fulfilled', value: '成功1' },
// { status: 'fulfilled', value: '成功2' },
// { status: 'rejected', reason: '失敗3' },
// { status: 'rejected', reason: '失敗4' }
// ]
// 處理結(jié)果
const successful = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
const failed = results
.filter(r => r.status === 'rejected')
.map(r => r.reason);
console.log('成功的:', successful); // ['成功1', '成功2']
console.log('失敗的:', failed); // ['失敗3', '失敗4']
});
?? 實際應(yīng)用場景
場景1:多個API請求(使用Promise.all)
// 需要所有用戶數(shù)據(jù)都加載成功才能顯示頁面
async function loadUserDashboard(userId) {
try {
const [userInfo, userOrders, userPreferences] = await Promise.all([
fetch(`/api/users/${userId}`).then(r => r.json()),
fetch(`/api/users/${userId}/orders`).then(r => r.json()),
fetch(`/api/users/${userId}/preferences`).then(r => r.json())
]);
// 所有數(shù)據(jù)都成功獲取,渲染儀表板
renderDashboard(userInfo, userOrders, userPreferences);
} catch (error) {
// 任何一個API失敗就顯示錯誤頁面
showErrorPage('加載用戶數(shù)據(jù)失敗');
}
}
場景2:批量圖片上傳(使用Promise.allSettled)
// 圖片上傳,希望知道每個文件的上傳結(jié)果
async function uploadMultipleImages(files) {
const uploadPromises = files.map(file =>
uploadImage(file).catch(error => {
// 捕獲單個上傳錯誤,但繼續(xù)其他上傳
return { error: error.message, fileName: file.name };
})
);
const results = await Promise.allSettled(uploadPromises);
const summary = {
successful: [],
failed: []
};
results.forEach(result => {
if (result.status === 'fulfilled') {
if (result.value.error) {
summary.failed.push({
fileName: result.value.fileName,
error: result.value.error
});
} else {
summary.successful.push(result.value);
}
} else {
summary.failed.push({
error: result.reason.message
});
}
});
console.log(`上傳完成: ${summary.successful.length} 成功, ${summary.failed.length} 失敗`);
return summary;
}
場景3:表單多個驗證(混合使用)
async function validateForm(formData) {
// 這些驗證可以并行執(zhí)行,但需要全部通過
const validationPromises = [
validateEmail(formData.email),
validatePassword(formData.password),
validateUsername(formData.username)
];
try {
const results = await Promise.all(validationPromises);
return { isValid: true, errors: [] };
} catch (error) {
// 但我們需要知道所有驗證錯誤,而不僅僅是第一個
// 所以更好的做法是使用 allSettled
const results = await Promise.allSettled(validationPromises);
const errors = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
return { isValid: false, errors };
}
}
?? 實用工具函數(shù)
1. 帶超時的Promise.all
function promiseAllWithTimeout(promises, timeoutMs) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('操作超時')), timeoutMs);
});
return Promise.all([...promises, timeoutPromise]);
}
2. 分類處理結(jié)果
function categorizeResults(results) {
return {
fulfilled: results.filter(r => r.status === 'fulfilled').map(r => r.value),
rejected: results.filter(r => r.status === 'rejected').map(r => r.reason),
all: results
};
}
// 使用
Promise.allSettled(promises)
.then(categorizeResults)
.then(({ fulfilled, rejected }) => {
console.log(`成功: ${fulfilled.length}, 失敗: ${rejected.length}`);
});
3. 逐步降級策略
async function fallbackStrategy(primaryAction, fallbackActions) {
try {
return await primaryAction();
} catch (error) {
// 主操作失敗,嘗試所有備用方案
const results = await Promise.allSettled(fallbackActions.map(action => action()));
const firstSuccess = results.find(result => result.status === 'fulfilled');
if (firstSuccess) {
return firstSuccess.value;
}
// 所有備用方案都失敗
throw new Error('所有操作都失敗了');
}
}
?? 選擇指南
使用 Promise.all 當(dāng):
- 所有操作都必須成功才能繼續(xù)
- 操作之間有依賴關(guān)系
- 需要原子性操作(要么全成功,要么全失?。?/li>
使用 Promise.allSettled 當(dāng):
- 需要知道每個操作的最終狀態(tài)
- 操作之間相互獨立
- 希望部分失敗不影響其他操作的結(jié)果收集
- 進(jìn)行批量操作,需要生成匯總報告
記住這個簡單的規(guī)則:"要么都要,要么都不要"用all,"我全都要知道"用allSettled。

浙公網(wǎng)安備 33010602011771號