Promise静态方法原理深度解析
all → 全赢才赢,race → 第一个落定说了算,allSettled → 不等输赢全汇总,any → 有一个赢就赢。四句话搞定四个静态方法。
一句话概括
Promise.all、Promise.race、Promise.allSettled、Promise.any 是四种并发语义的标准化表达——all 求全、race 求快、allSettled 求稳、any 求活。
核心知识点
1. Promise.all — 全部成功才成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Promise.myAll = function (iterable) {
const promises = Array.from(iterable);
return new Promise((resolve, reject) => {
if (promises.length === 0) return resolve([]);
const results = new Array(promises.length);
let count = 0;
promises.forEach((p, i) => {
Promise.resolve(p).then(
v => {
results[i] = v; // 👈 保持输入顺序,不是完成顺序
if (++count === promises.length) resolve(results);
},
e => reject(e) // 👈 一个失败立即全盘否决
);
});
});
};
// 测试
const slow = new Promise(r => setTimeout(() => r('slow'), 200));
const fast = new Promise(r => setTimeout(() => r('fast'), 50));
Promise.myAll([slow, fast]).then(console.log); // ['slow', 'fast']——顺序和输入一致!
三个细节:空数组直接 resolve []、结果保序、任一失败短路。
2. Promise.race — 第一个落定决定结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Promise.myRace = function (iterable) {
return new Promise((resolve, reject) => {
for (const p of iterable) {
Promise.resolve(p).then(resolve, reject);
// 后续的 resolve/reject 调用会被 Promise 的状态锁忽略
}
});
};
// 超时控制 —— race 的经典用法
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('超时')), ms)
);
return Promise.race([promise, timeout]);
}
race 不区分成功失败——谁先到就用谁。空数组传进去会永远 pending。
3. Promise.allSettled — 等所有人跑完
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Promise.myAllSettled = function (iterable) {
const promises = Array.from(iterable);
return new Promise(resolve => {
if (promises.length === 0) return resolve([]);
const results = new Array(promises.length);
let count = 0;
promises.forEach((p, i) => {
Promise.resolve(p).then(
v => { results[i] = { status: 'fulfilled', value: v }; if (++count === promises.length) resolve(results); },
e => { results[i] = { status: 'rejected', reason: e }; if (++count === promises.length) resolve(results); }
);
});
});
};
永远不会 reject。适合批量操作后汇总结果——成功的有哪些、失败的原因是什么。
4. Promise.any — 有一个成功就成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Promise.myAny = function (iterable) {
const promises = Array.from(iterable);
return new Promise((resolve, reject) => {
if (promises.length === 0) {
return reject(new AggregateError([], 'All promises were rejected'));
}
const errors = new Array(promises.length);
let count = 0;
promises.forEach((p, i) => {
Promise.resolve(p).then(
v => resolve(v), // 👈 任一成功直接赢
e => {
errors[i] = e;
if (++count === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
);
});
});
};
any 和 race 的关键区别:race 遇到第一个 reject 就认输,any 会继续等,直到有成功的或全部失败。
5. 一张表记住四个方法
| 方法 | 成功条件 | 失败条件 | 返回值 | ES 版本 |
|---|---|---|---|---|
all | 全部 resolve | 任一 reject | 结果数组 | ES2015 |
race | 第一个落定 | 第一个落定 | 第一个结果 | ES2015 |
allSettled | 全部落定 | 永不失败 | {status, value/reason}[] | ES2020 |
any | 任一 resolve | 全部 reject | 第一个成功值 | ES2021 |
「其实你每天都在用」
1. 页面初始化并发加载
1
2
3
4
5
6
const [user, config, permissions] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/config').then(r => r.json()),
fetch('/api/permissions').then(r => r.json()),
]);
// 三个请求同时飞出去,等最慢的回来
2. CDN 多节点容灾
1
2
const cdnUrls = ['https://cdn1.com/file.js', 'https://cdn2.com/file.js', 'https://cdn3.com/file.js'];
const content = await Promise.any(cdnUrls.map(url => fetch(url).then(r => r.text())));
3. 请求超时
1
2
3
4
const data = await Promise.race([
fetch('/api/slow').then(r => r.json()),
new Promise((_, r) => setTimeout(() => r(new Error('超时')), 5000)),
]);
4. 批量操作结果汇总
1
2
3
const results = await Promise.allSettled(ids.map(id => deleteItem(id)));
const failed = results.filter(r => r.status === 'rejected');
console.log(`成功 ${results.length - failed.length} 个,失败 ${failed.length} 个`);
5. 多个搜索源竞速
1
2
3
4
5
// 同时搜本地缓存和远程 API,哪个快用哪个
const result = await Promise.any([
searchCache(query),
searchRemote(query),
]);
常见误解(FAQ)
❌ 误区 1:「Promise.all 里一个失败其他请求会被取消」
不会。Promise.all 只是不再等待,但已发出的网络请求无法撤销。真正需要取消,要配合 AbortController。
❌ 误区 2:「Promise.race 空数组和 Promise.all 空数组行为一样」
不一样。Promise.all([]) 立即 resolve []。Promise.race([]) 永远 pending——因为没有 Promise 能落定。Promise.any([]) 立即 reject AggregateError。
❌ 误区 3:「Promise.any 返回第一个成功,其他的还在跑」
其他 Promise 仍然在跑,只是结果被忽略了。和 Promise.all 一样,any 不会取消已启动的异步操作。
❌ 误区 4:「allSettled 和 all 的错误处理一样」
完全不同。all 遇到错误立即 reject,不会告诉你哪些成功了。allSettled 等所有人跑完,给你完整名单。简单说:一个只报坏消息,一个报所有的好消息和坏消息。
一句话总结
四个方法就是一个 2×2 矩阵:要全队还是只要一个?(all vs any/race)× 在乎失败还是不在乎?(all/race vs allSettled/any)。想清楚你的业务场景,自然知道选哪个。