Promise组合
本章将详细介绍Promise的组合方法,包括Promise.all、Promise.race、Promise.allSettled等,帮助您掌握如何并行或竞争性地执行多个异步操作。
5.1 Promise.all
Promise.all方法用于并行执行多个Promise,当所有Promise都成功时,返回包含所有结果的数组;当任何一个Promise失败时,立即拒绝。
基本语法
示例:Promise.all基本用法
// Promise.all的基本语法
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log(results); // 输出: [1, 2, 3]
})
.catch(error => {
console.error('其中一个Promise失败:', error);
});
实际应用场景
Promise.all常用于需要同时获取多个资源的场景。
示例:并行获取用户信息
// 模拟API调用
function fetchUser(userId) {
return new Promise(resolve => {
setTimeout(() => {
resolve({ id: userId, name: `用户${userId}` });
}, Math.random() * 1000);
});
}
// 并行获取多个用户信息
const userIds = [1, 2, 3, 4, 5];
Promise.all(userIds.map(id => fetchUser(id)))
.then(users => {
console.log('所有用户信息:', users);
// 所有用户信息获取完成后进行处理
displayUsers(users);
})
.catch(error => {
console.error('获取用户信息失败:', error);
});
错误处理
Promise.all具有"快速失败"的特性,任何一个Promise被拒绝,整个Promise.all都会被拒绝。
示例:Promise.all错误处理
const promise1 = Promise.resolve('成功1');
const promise2 = Promise.reject(new Error('失败2'));
const promise3 = Promise.resolve('成功3');
Promise.all([promise1, promise2, promise3])
.then(results => {
// 这里不会执行
console.log(results);
})
.catch(error => {
console.error('Promise.all失败:', error.message); // 输出: Promise.all失败: 失败2
});
5.2 Promise.race
Promise.race方法用于竞争性执行多个Promise,返回第一个完成(无论是成功还是失败)的Promise的结果。
基本语法
示例:Promise.race基本用法
// Promise.race的基本语法
const promise1 = new Promise(resolve => setTimeout(() => resolve(1), 1000));
const promise2 = new Promise(resolve => setTimeout(() => resolve(2), 2000));
const promise3 = new Promise(resolve => setTimeout(() => resolve(3), 3000));
Promise.race([promise1, promise2, promise3])
.then(result => {
console.log(result); // 输出: 1 (第一个完成的Promise)
});
实际应用场景
Promise.race常用于实现超时控制。
示例:实现请求超时控制
// 模拟网络请求
function fetchUserData(userId) {
return new Promise(resolve => {
setTimeout(() => {
resolve({ id: userId, name: `用户${userId}` });
}, 2000); // 模拟2秒的网络延迟
});
}
// 超时Promise
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('请求超时')), ms);
});
}
// 带超时控制的请求
Promise.race([
fetchUserData(123),
timeout(1500) // 1.5秒超时
])
.then(userData => {
console.log('获取用户数据成功:', userData);
})
.catch(error => {
if (error.message === '请求超时') {
console.error('请求超时,请检查网络连接');
} else {
console.error('获取用户数据失败:', error);
}
});
5.3 Promise.allSettled
Promise.allSettled方法在ES2020中引入,等待所有Promise都完成(无论是成功还是失败),返回包含每个Promise结果的对象数组。
基本语法
示例:Promise.allSettled基本用法
const promise1 = Promise.resolve('成功1');
const promise2 = Promise.reject(new Error('失败2'));
const promise3 = Promise.resolve('成功3');
Promise.allSettled([promise1, promise2, promise3])
.then(results => {
console.log(results);
/*
输出:
[
{ status: 'fulfilled', value: '成功1' },
{ status: 'rejected', reason: Error: 失败2 },
{ status: 'fulfilled', value: '成功3' }
]
*/
});
实际应用场景
Promise.allSettled适用于需要等待所有操作完成,但不希望因为某个操作失败而中断其他操作的场景。
示例:批量处理数据
// 模拟数据处理函数
function processData(item) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.3) {
resolve(`处理成功: ${item}`);
} else {
reject(new Error(`处理失败: ${item}`));
}
}, 1000);
});
}
// 批量处理数据
const dataItems = ['数据1', '数据2', '数据3', '数据4', '数据5'];
Promise.allSettled(dataItems.map(item => processData(item)))
.then(results => {
const successful = results
.filter(result => result.status === 'fulfilled')
.map(result => result.value);
const failed = results
.filter(result => result.status === 'rejected')
.map(result => result.reason.message);
console.log('成功处理:', successful);
console.log('处理失败:', failed);
// 继续处理成功的数据
handleSuccessfulData(successful);
// 记录失败的数据
logFailedData(failed);
});
5.4 Promise.any
Promise.any方法在ES2021中引入,等待第一个成功的Promise,如果所有Promise都失败,则拒绝。
基本语法
示例:Promise.any基本用法
const promise1 = Promise.reject(new Error('失败1'));
const promise2 = Promise.resolve('成功2');
const promise3 = Promise.reject(new Error('失败3'));
Promise.any([promise1, promise2, promise3])
.then(result => {
console.log(result); // 输出: 成功2
})
.catch(error => {
console.error('所有Promise都失败:', error);
});
实际应用场景
Promise.any常用于实现冗余请求,向多个服务器发送请求,使用第一个成功的响应。
示例:冗余请求
// 模拟向不同服务器发送请求
function fetchFromServer(server) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve(`来自${server}的数据`);
} else {
reject(new Error(`${server}请求失败`));
}
}, Math.random() * 2000);
});
}
// 向多个服务器发送请求,使用第一个成功的响应
const servers = ['服务器A', '服务器B', '服务器C'];
Promise.any(servers.map(server => fetchFromServer(server)))
.then(data => {
console.log('获取数据成功:', data);
// 使用第一个成功的响应
useData(data);
})
.catch(error => {
console.error('所有服务器都不可用:', error);
// 所有服务器都失败时的处理
showErrorMessage('暂时无法获取数据,请稍后重试');
});
5.5 组合方法的比较
不同Promise组合方法的特性和使用场景对比:
方法 | 成功条件 | 失败条件 | 返回值 | 适用场景 |
---|---|---|---|---|
Promise.all | 所有Promise都成功 | 任一Promise失败 | 成功值数组 | 需要所有操作都成功 |
Promise.race | 第一个Promise完成 | 第一个Promise失败 | 第一个完成的值 | 超时控制、竞争条件 |
Promise.allSettled | 所有Promise完成 | 无 | 所有结果对象数组 | 需要等待所有操作完成 |
Promise.any | 任一Promise成功 | 所有Promise失败 | 第一个成功的值 | 冗余请求、容错处理 |
5.6 高级组合技巧
在实际开发中,可以结合使用多种Promise组合方法来实现复杂的需求。
分批处理
对于大量数据,可以分批处理以避免资源耗尽。
示例:分批处理数据
// 分批处理函数
function batchProcess(data, batchSize) {
const batches = [];
for (let i = 0; i < data.length; i += batchSize) {
batches.push(data.slice(i, i + batchSize));
}
return batches.reduce((promise, batch) => {
return promise.then(results => {
return Promise.all(batch.map(item => processItem(item)))
.then(batchResults => [...results, ...batchResults]);
});
}, Promise.resolve([]));
}
// 使用分批处理
const largeDataSet = Array.from({ length: 1000 }, (_, i) => `数据${i}`);
batchProcess(largeDataSet, 100)
.then(allResults => {
console.log('所有数据处理完成:', allResults.length);
})
.catch(error => {
console.error('处理过程中出错:', error);
});
带重试机制的组合
为提高成功率,可以为Promise添加重试机制。
示例:带重试机制的Promise组合
// 带重试机制的Promise
function retryPromise(promiseFn, maxRetries = 3) {
return new Promise((resolve, reject) => {
const attempt = (retryCount) => {
promiseFn()
.then(resolve)
.catch(error => {
if (retryCount < maxRetries) {
console.log(`第${retryCount + 1}次尝试失败,正在重试...`);
attempt(retryCount + 1);
} else {
reject(new Error(`重试${maxRetries}次后仍然失败: ${error.message}`));
}
});
};
attempt(0);
});
}
// 使用带重试机制的组合
const unreliablePromises = [
() => fetchUnreliableData(1),
() => fetchUnreliableData(2),
() => fetchUnreliableData(3)
];
Promise.all(unreliablePromises.map(promiseFn => retryPromise(promiseFn)))
.then(results => {
console.log('所有数据获取成功:', results);
})
.catch(error => {
console.error('即使重试后仍然失败:', error);
});
5.7 组合方法的错误处理
不同组合方法的错误处理方式有所不同,需要根据具体场景选择合适的处理方式。
示例:组合方法的错误处理
// Promise.all的错误处理
Promise.all(promises)
.then(results => {
// 所有Promise都成功
handleSuccess(results);
})
.catch(error => {
// 至少一个Promise失败
handleAllFailure(error);
});
// Promise.allSettled的错误处理
Promise.allSettled(promises)
.then(results => {
// 分别处理成功和失败的情况
const successful = results.filter(r => r.status === 'fulfilled');
const failed = results.filter(r => r.status === 'rejected');
handlePartialSuccess(successful, failed);
});
// Promise.any的错误处理
Promise.any(promises)
.then(result => {
// 至少一个Promise成功
handleAnySuccess(result);
})
.catch(error => {
// 所有Promise都失败
if (error instanceof AggregateError) {
handleAllFailed(error.errors);
} else {
handleAllFailed([error]);
}
});
提示:在使用Promise组合方法时,要根据业务需求选择合适的方法。如果需要所有操作都成功,使用Promise.all;如果只需要一个成功结果,使用Promise.any;如果需要等待所有操作完成无论成功失败,使用Promise.allSettled。