5月27日 23:32

Promise 有哪几种状态?状态如何转换?

状态与转换规则

Promise 有三种核心状态,理解状态转换是掌握 Promise 的基础:

  • pending:初始状态,异步操作尚未完成
  • fulfilled:操作成功,触发 .then() 回调
  • rejected:操作失败,触发 .catch() 回调

状态转换只能发生一次:pending → fulfilled 或 pending → rejected,一旦改变不可逆。多次调用 resolvereject,只有第一次生效。

此外还有一个派生状态 settled(已定型),表示 Promise 已完成(无论成功或失败),此时会触发 .finally() 回调。settled 不是独立状态,而是 fulfilled 和 rejected 的统称。

javascript
const p = new Promise((resolve, reject) => { resolve('第一次'); // 生效,状态变为 fulfilled resolve('第二次'); // 忽略,状态已不可变 reject('失败'); // 忽略 }); p.then(val => console.log(val)); // "第一次"

then 返回值与新 Promise 状态

.then() 返回的是一个新的 Promise,它的状态由回调函数的返回值决定:

  • 返回普通值 → 新 Promise 变为 fulfilled,值为该返回值
  • 抛出错误 → 新 Promise 变为 rejected
  • 返回另一个 Promise → 新 Promise 的状态跟随该 Promise
javascript
Promise.resolve(1) .then(val => val + 1) // 返回 2,新 Promise fulfilled .then(val => { // val 为 2 throw new Error('出错了'); // 新 Promise rejected }) .catch(err => 100) // 捕获错误,返回 100 .then(val => console.log(val)); // 100

错误处理与穿透机制

Promise 的错误会沿链向下传递,直到遇到 .catch()。如果 .then() 没有提供第二个参数(错误回调),错误会自动穿透到下一个 .catch()

javascript
fetch('/api/data') .then(res => res.json()) // 如果 fetch 失败,错误穿透到 catch .then(data => processData(data)) // 如果上一步失败,继续穿透 .catch(err => console.error('请求失败:', err));

注意:.then(onFulfilled, onRejected) 中,onRejected 只捕获前一步的错误,不能捕获 onFulfilled 自身的错误。推荐统一使用 .catch()

静态方法对比

方法全部成功有失败返回值
Promise.all()返回所有结果第一个失败的原因数组
Promise.allSettled()返回所有状态和结果不会失败{status, value/reason}[]
Promise.race()第一个完成的结果第一个失败的原因单个值
Promise.any()第一个成功的结果全部失败时返回 AggregateError单个值

Promise.all() 适合"全部成功才继续"的场景(如并行请求多个接口)。Promise.allSettled() 适合"不管成败都要结果"(如批量操作后统计)。Promise.any() 适合"取最快成功的"(如多源竞速)。

微任务与执行顺序

Promise 的 .then()/.catch()/.finally() 回调属于微任务,在当前宏任务结束后、下一个宏任务开始前执行。微任务优先级高于宏任务:

javascript
console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4'); // 输出顺序:1 → 4 → 3 → 2

面试中常考的变体:在 then 回调中嵌套 setTimeout,或 async/awaitPromise.then 的混合执行顺序。

常见陷阱

1. then 中的 throw 被 catch 捕获,但同步代码中的 throw 不会:

javascript
Promise.resolve() .then(() => { throw new Error('then中抛出'); }) .catch(e => console.log('捕获:', e.message)); // 捕获: then中抛出

2. catch 之后还能继续 then:

.catch() 本身也返回 Promise,后续可以接 .then() 继续执行。

3. Promise 构造函数中的同步错误:

javascript
const p = new Promise(() => { throw new Error('构造函数中抛出'); // 会被 Promise 内部捕获,p 变为 rejected });

4. 返回值是 thenable 对象(有 then 方法的对象)会被当作 Promise 处理:

javascript
Promise.resolve().then(() => { return { then(resolve) { resolve('thenable'); } }; }).then(val => console.log(val)); // "thenable"

async/await 本质

async/await 是 Promise 的语法糖。async 函数始终返回 Promise,await 暂停执行直到 Promise 完成。错误处理使用 try/catch,比 .catch() 更符合同步代码的直觉写法:

javascript
async function fetchUser() { try { const res = await fetch('/api/user'); const user = await res.json(); return user; } catch (err) { console.error('获取用户失败:', err); } }

注意:await 只能在 async 函数内使用(顶层 await 需 ES2022 模块环境)。多个独立异步操作不要串行 await,应使用 Promise.all() 并行处理。

标签:Promise