一次讲透 JS Promise —— 状态、用法和 4 个静态方法

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:第一个成功的就用
相关推荐
asinnn1 小时前
8GB 视频只读取 66KB?聊聊浏览器端大文件低内存元数据修改的实现原理
javascript
沙蒿同学1 小时前
Wails v2 实战:用 Go + Vue3 做一个真正能用的 AI 桌面应用
前端·javascript·后端
三十而立洋5 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁5 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄6 天前
JavaScript 隐式全局变量解析
javascript
沙蒿同学6 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端
paopaokaka_luck6 天前
智慧社区综合服务小程序(人脸识别、AI问答、Echarts图形化分析)
前端·javascript·spring boot·spring·数据分析·echarts
Hilaku6 天前
为什么死磕 1px 的团队,用户体验反而更差?
前端·javascript·程序员
陈拉斯6 天前
给公司年会写了个大屏抽奖系统:动画在前端跑,凭什么说结果没被改?
javascript