【面试高频】手写 Promise 四大并发方法
Promise 的并发方法: all,allSettled,any,race。这些方法都会接受 promises 数组,返回一个新的 Promise。
Promise.all
成功:所有传入的 promise 状态都为 fulfilled,传回所有的成功的结果数组
失败:只要有一个 promise 状态为 rejected,直接传回失败结果
核心特征(独立、缺一不可):
-
业务逻辑依赖所有任务的成功结果(缺一不可)
-
快速失败(任一任务失败则整体终止,无需等待其他任务)
适用场景:
-
资源预先加载
-
依赖多接口数据加载
function PromiseAll(promises) {return new Promise((resolve, reject) => {if(!Array.isArray(promises)) {reject(new TypeError('输入参数错误'));}let n = promises.length;let results = new Array(n);let fullfilledCnt = 0;if(n == 0) {return resolve(results);}promises.forEach((promise, i)=> {Promise.resolve(promise).then(result => {results[i] = result;fullfilledCnt ++;if(fullfilledCnt == n) {resolve(results);}}).catch(err => {reject(err);});});});
};// const p1 = Promise.resolve(1);
// const p2 = new Promise(resolve => setTimeout(() => resolve(2), 1000));
// PromiseAll([p1, p2]).then(console.log); // [1, 2](约1m后)// const p3 = Promise.reject('出错了');
// PromiseAll([p1, p3]).catch(console.error); // '出错了'(立即)
Promise.allSettled
成功:所有传入的 promise 状态都为 fulfilled 或 rejected,传回结果为数组,包含状态以及成功或失败的结果
核心特征:完整结果反馈; 不因单个失败而中断
适用场景:
-
日志上传
-
非核心资源预加载
function PromiseAllSettled(promises) {return new Promise((resolve, reject) => {if(!Array.isArray(promises)) {reject(new TypeError('输入参数错误'));}let n = promises.length;let results = new Array(n);let cnt = 0;if(n == 0) {return resolve(results);}promises.forEach((promise, i)=> {Promise.resolve(promise).then(value => {results[i] = { stats: 'fullfilled', value };}).catch(error => {results[i] = { stats: 'rejected', error };}).finally(() => {cnt ++;if(cnt == n) {resolve(results);}});});});
};// const promise1 = Promise.resolve(3);
// const promise2 = Promise.reject(new Error('Promise 2 failed'));
// const promise3 = new Promise((resolve) => {
// setTimeout(resolve, 100, 'foo');
// });// PromiseAll([promise1, promise2, promise3])
// .then(values => {
// console.log(values); // 不会执行
// })
// .catch(error => {
// console.error(error); // 输出: Error: Promise 2 failed
// });
Promise.any
成功:第一个完成的 promise 的状态为 fulfilled,返回成功结果
失败:所有 promise 状态为 rejected,返回失败的数组
特征:结果单一性;全败才败(兜底)
场景:多源数据兜底获取、多节点服务器可用性检测
function PromiseAny(promises) {return new Promise((resolve, reject) => {if(!Array.isArray(promises)) {reject(new TypeError('输入参数错误'));}let n = promises.length;if(n == 0) return;let rejectCnt = 0;let errors = new Array(n);promises.forEach((promise, i)=> {Promise.resolve(promise).then(value => {resolve(value);}).catch(error => {rejectCnt ++;errors[i] = errorif(rejectCnt == n) {reject(new AggregateError(error, "所有promises都被rejected"));}});});});
};// const resolveAfter = (value, delay) =>
// new Promise(resolve => setTimeout(() => resolve(value), delay));// const rejectAfter = (reason, delay) =>
// new Promise((_, reject) => setTimeout(() => reject(reason), delay));// // 有一个成功(第一个成功的结果被返回)
// const test1 = async () => {
// const p1 = rejectAfter('p1失败', 100);
// const p2 = resolveAfter('p2成功', 200);
// const p3 = resolveAfter('p3成功', 150);
// try {
// const result = await PromiseAny([p1, p2, p3]);
// console.log('测试1结果:', result);
// } catch (err) {
// console.error('测试1错误:', err);
// }
// };// test1();
Promise.race
成功:第一个完成的 promise 的状态为 fulfilled,返回成功结果
失败:第一个完成的 promise 的状态为 rejected,返回失败结果
特征:竞速性,最快的响应结果,结果具有单一性
场景:cdn 命中(要的就是最快的响应);用户请求同一接口(避免重复刷新);接口请求超时
function PromiseRace(promises) {return new Promise((resolve, reject) => {if(!Array.isArray(promises)) {reject(new TypeError('输入参数错误'));}let n = promises.length;if(n == 0) return;promises.forEach((promise, i)=> {Promise.resolve(promise).then(value => {resolve(value);}).catch(error => {reject(error);});});});
};// // 测试正常场景
// const p1 = new Promise(resolve => setTimeout(() => resolve(1), 100));
// const p2 = Promise.resolve(2);
// PromiseRace([p1, p2]).then(console.log); // 2(立即,p2先成功)// // 测试失败场景
// const p3 = Promise.reject('出错了');
// const p4 = new Promise(resolve => setTimeout(resolve, 200));
// PromiseRace([p4, p3]).catch(console.error); // '出错了'(立即,p3先失败)
