Promise是JS异步编程的核心,Promise.all和Promise.race更是面试常考的手写题。
今天从零实现这两个方法,顺便理解Promise的设计哲学。
Promise.all:全部成功才成功
Promise.all接收一个Promise数组,所有都fulfilled才返回结果数组,任意一个rejected就失败。
javascript
// 用法
const results = await Promise.all([
fetchUser(1),
fetchOrders(1),
fetchConfig()
]);
// results = [user, orders, config]
手写实现:
javascript
function promiseAll(promises) {
return new Promise((resolve, reject) => {
// 不是数组,报错
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
const results = [];
let resolvedCount = 0;
const total = promises.length;
// 空数组直接resolve
if (total === 0) {
return resolve([]);
}
promises.forEach((promise, index) => {
// 包装非Promise值
Promise.resolve(promise).then(
(value) => {
results[index] = value; // 注意:用index保证顺序
resolvedCount++;
if (resolvedCount === total) {
resolve(results);
}
},
(reason) => {
reject(reason); // 任意一个失败就reject
}
);
});
});
}
// 测试
promiseAll([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(console.log); // [1, 2, 3]
promiseAll([
Promise.resolve(1),
Promise.reject('error'),
Promise.resolve(3)
]).catch(console.log); // 'error'
关键点:
- 结果数组的顺序和输入顺序一致(不是按完成顺序)
- 任意一个rejected就立即reject
- 非Promise值会被
Promise.resolve包装
Promise.race:谁快用谁
Promise.race返回最先完成的那个Promise的结果,不管成功还是失败。
javascript
// 用法:超时控制
const result = await Promise.race([
fetchData(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), 5000)
)
]);
手写实现:
javascript
function promiseRace(promises) {
return new Promise((resolve, reject) => {
for (const promise of promises) {
Promise.resolve(promise).then(
(value) => resolve(value), // 第一个完成的直接resolve
(reason) => reject(reason) // 第一个失败的直接reject
);
}
});
}
// 测试:超时控制
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), timeout)
)
]);
}
Promise.allSettled:等全部完成
Promise.allSettled等所有Promise都完成(不管成败),返回每个的状态和结果。
javascript
const results = await Promise.allSettled([
Promise.resolve('成功'),
Promise.reject('失败'),
Promise.resolve('又成功')
]);
// [
// { status: 'fulfilled', value: '成功' },
// { status: 'rejected', reason: '失败' },
// { status: 'fulfilled', value: '又成功' }
// ]
手写实现:
javascript
function promiseAllSettled(promises) {
return new Promise((resolve) => {
const results = [];
let completed = 0;
const total = promises.length;
if (total === 0) return resolve([]);
promises.forEach((promise, index) => {
Promise.resolve(promise).then(
(value) => {
results[index] = { status: 'fulfilled', value };
},
(reason) => {
results[index] = { status: 'rejected', reason };
}
).finally(() => {
completed++;
if (completed === total) {
resolve(results);
}
});
});
});
}
Promise.any:第一个成功的
Promise.any返回第一个成功的Promise,全部失败才reject。
javascript
function promiseAny(promises) {
return new Promise((resolve, reject) => {
const errors = [];
let rejectedCount = 0;
const total = promises.length;
if (total === 0) {
return reject(new AggregateError([], 'All promises were rejected'));
}
promises.forEach((promise, index) => {
Promise.resolve(promise).then(
(value) => resolve(value),
(reason) => {
errors[index] = reason;
rejectedCount++;
if (rejectedCount === total) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
);
});
});
}
四个方法对比
| 方法 | 成功条件 | 失败条件 | 返回 |
|---|---|---|---|
| Promise.all | 全部成功 | 任意一个失败 | 结果数组 |
| Promise.race | 第一个完成 | 第一个失败 | 单个结果 |
| Promise.allSettled | 全部完成(不管成败) | 不会失败 | 状态+结果数组 |
| Promise.any | 任意一个成功 | 全部失败 | 单个结果 |
手写Promise系列是面试高频题,核心就是理解Promise的构造和状态管理。把这几个实现搞懂,Promise就不再是难点了。
建议自己在编辑器里写一遍,比看十遍都管用。