Promise 有哪几种状态?状态如何转换?
状态与转换规则
Promise 有三种核心状态,理解状态转换是掌握 Promise 的基础:
- pending:初始状态,异步操作尚未完成
- fulfilled:操作成功,触发
.then()回调 - rejected:操作失败,触发
.catch()回调
状态转换只能发生一次:pending → fulfilled 或 pending → rejected,一旦改变不可逆。多次调用 resolve 或 reject,只有第一次生效。
此外还有一个派生状态 settled(已定型),表示 Promise 已完成(无论成功或失败),此时会触发 .finally() 回调。settled 不是独立状态,而是 fulfilled 和 rejected 的统称。
javascriptconst 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
javascriptPromise.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():
javascriptfetch('/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() 回调属于微任务,在当前宏任务结束后、下一个宏任务开始前执行。微任务优先级高于宏任务:
javascriptconsole.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4'); // 输出顺序:1 → 4 → 3 → 2
面试中常考的变体:在 then 回调中嵌套 setTimeout,或 async/await 与 Promise.then 的混合执行顺序。
常见陷阱
1. then 中的 throw 被 catch 捕获,但同步代码中的 throw 不会:
javascriptPromise.resolve() .then(() => { throw new Error('then中抛出'); }) .catch(e => console.log('捕获:', e.message)); // 捕获: then中抛出
2. catch 之后还能继续 then:
.catch() 本身也返回 Promise,后续可以接 .then() 继续执行。
3. Promise 构造函数中的同步错误:
javascriptconst p = new Promise(() => { throw new Error('构造函数中抛出'); // 会被 Promise 内部捕获,p 变为 rejected });
4. 返回值是 thenable 对象(有 then 方法的对象)会被当作 Promise 处理:
javascriptPromise.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() 更符合同步代码的直觉写法:
javascriptasync 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() 并行处理。