Promise 本质是一个状态机对象,用来处理异步。
这篇整理:三种状态、new Promise 的用法、then/catch/finally,以及最常用的 4 个静态方法(all、allSettled、race、any)。
基本用法:
js
const p = new Promise((resolve, reject) => {
setTimeout(() => resolve('成功'), 1000);
});
p.then(res => console.log(res)); // 1 秒后输出:成功
一、三种状态
Promise 有且只有三种状态:
- pending:进行中(初始状态)
- fulfilled:已成功(调用 resolve 后)
- rejected:已失败(调用 reject 或抛错后)
状态一旦确定,不可再变:
js
const p = new Promise((resolve, reject) => {
resolve('第一次');
resolve('第二次'); // 无效,状态已是 fulfilled
reject('第三次'); // 无效,状态已是 fulfilled
});
p.then(res => console.log(res)); // "第一次"
二、new Promise 用法
new Promise 接受一个执行器函数 ,里面有两个参数:resolve 和 reject。
js
const p = new Promise((resolve, reject) => {
someAsyncTask((err, result) => {
if (err) {
reject(err); // 失败
} else {
resolve(result); // 成功
}
});
});
执行器函数是同步执行的:
js
console.log(1);
new Promise(resolve => {
console.log(2); // 同步执行
resolve();
}).then(() => console.log(3));
console.log(4);
// 输出:1 2 4 3
三、then / catch / finally
then 拿成功结果
js
Promise.resolve(1).then(v => console.log(v)); // 1
catch 拿错误
js
Promise.reject(new Error('失败')).catch(err => console.log(err.message)); // "失败"
finally 做清理(不管成败都执行)
js
fetch('/api')
.then(res => console.log(res))
.catch(err => console.log(err))
.finally(() => hideLoading());
四、Promise 的 4 个静态方法
Promise 提供了 4 个静态方法,用于处理多个 Promise:
| 方法 | 场景 |
|---|---|
| Promise.all | 全部成功才要 |
| Promise.allSettled | 全部结果,不管成败 |
| Promise.race | 谁先有结果就用谁 |
| Promise.any | 只要有一个成功就够 |
下面逐个详细看。
五、Promise.all(并发,全部成功才要)
场景:同时请求多个 API,等全部完成。
js
const [user, config, permissions] = await Promise.all([
getUser(),
getConfig(),
getPermissions(),
]);
规则:
- 传入 Promise 数组
- 全部成功 → 返回结果数组(顺序和传入时一致)
- 任何一个失败 → 立刻 reject,其他结果丢弃
注意:一个失败,全部丢弃。不适合"某个允许失败"的场景。
六、Promise.allSettled(容错并发,不管成败都要结果)
场景:Dashboard 有 10 个图表接口,某个失败不应该影响其他显示。
js
const results = await Promise.allSettled(charts);
results.forEach((result, i) => {
if (result.status === 'fulfilled') {
console.log(`图表 ${i}:`, result.value);
} else {
console.log(`图表 ${i} 失败:`, result.reason);
}
});
返回结构:
js
[
{ status: 'fulfilled', value: '成功结果' },
{ status: 'rejected', reason: '失败原因' },
// ...
]
和 Promise.all 的区别:all 要求全部成功,allSettled 不管成败都会等所有 Promise 走完,再把每个的结果(或失败原因)一起返回。
七、Promise.race(谁先有结果就用谁)
场景:超时控制。
js
function fetchWithTimeout(url, ms) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), ms)
),
]);
}
fetchWithTimeout('/api/slow', 5000).catch(err => console.log(err.message));
规则 :传入的 Promise 里,第一个有结果的 (不管是成功还是失败)就是 race 的结果,其他的直接忽略。
上面例子里,如果 fetch(url) 5 秒内没完成,setTimeout 那个 Promise 会先 reject,race 就直接报"请求超时"。
八、Promise.any(只要有一个成功就够)
场景:多个 CDN 源存了同一份数据,只要拿到其中任意一个成功的结果就行。
js
const url = await Promise.any([
fetch('https://cdn1.com/data'),
fetch('https://cdn2.com/data'),
fetch('https://cdn3.com/data'),
]);
规则 :只要传入的 Promise 里有一个成功 ,就返回那个成功的结果,不需要等其他的,也不在乎其他的是否失败。只有全部失败,才会 reject。
和 Promise.race 的区别:
- race:第一个"有结果"的就用(失败也算"有结果")
- any:失败会被跳过,继续等,直到等到一个成功,或者全部失败
举个对比:如果 cdn1 很快返回但失败了,race 会直接把这个失败当结果返回;any 则会忽略这个失败,继续等 cdn2、cdn3,直到有一个成功。
九、几个常见坑
坑 1:forEach 里的 await 等不到
js
[1, 2, 3].forEach(async (id) => {
await fetch(`/api/${id}`);
});
console.log('完成'); // 立刻打印,不等请求
发生了什么:
async (id) => {...} 是一个 async 函数,调用后会返回一个 Promise。但 forEach 不会管回调的返回值是什么,拿到这个 Promise 也不会等它,循环立刻跑完,所以 console.log('完成') 马上执行,根本不等 3 个 fetch 完成。
改成 for...of(等待,一个个来):
js
for (const id of [1, 2, 3]) {
await fetch(`/api/${id}`);
}
console.log('完成'); // 等 3 个都完成才打印
改成 map + Promise.all(等待,同时进行):
js
await Promise.all([1, 2, 3].map(id => fetch(`/api/${id}`)));
console.log('完成');
这里 .map() 返回的是一个 Promise 数组 (因为每个 fetch 都是 Promise),Promise.all 才能等它们全部完成。
为什么 for...of 能等,forEach 不能等 :for...of 是原生循环语法,await 直接作用于当前函数,能真正暂停。forEach 是一个方法,await 只能暂停"传给它的那个回调函数自己",不能暂停 forEach 本身的循环。
坑 2:忘了 await 或 .then
js
function loadData() {
const p = fetch('/api'); // p 是 Promise,不是结果
console.log(p.data); // undefined
}
Promise 拿结果必须用 .then 或 await。
十、总结
- 三种状态:pending / fulfilled / rejected,一旦确定不可变
- new Promise:执行器函数里 resolve / reject 触发状态,执行器本身同步执行
- then / catch / finally:拿结果、拿错误、做清理
- 4 个静态方法 :
all:全部成功才要allSettled:不管成败都要结果race:第一个有结果的就用any:第一个成功的就用