讲解
Promise 是异步操作的「标准收据」:一个 Promise 对象代表一个尚未完成、未来会出结果的异步操作。它有三种状态:pending(进行中)、fulfilled(成功)、rejected(失败),且状态只能 pending → fulfilled 或 pending → rejected 单向流转一次,落定后不可更改。创建方式是 new Promise((resolve, reject) => {...}):执行器函数立即同步执行,成功时调 resolve(值),失败时调 reject(原因)。
消费 Promise 用三个方法:then(onFulfilled) 处理成功、catch(onRejected) 处理失败、finally 无论成败收尾。then 返回一个新的 Promise,于是可以链式书写 promise.then(...).then(...).catch(...)——每一步拿到上一步的返回值,任一环出错跳到最近的 catch,把回调地狱拉平成一列。then 里的回调总是在微任务里异步执行,即使 Promise 已经成功。
并发控制靠几个静态方法:Promise.all(全部成功才成功,一个失败整体失败,结果按传入顺序排列)、Promise.allSettled(等全部落定,成功失败各自记录,不短路)、Promise.race(谁先有结果用谁)、Promise.any(第一个成功的,全失败才失败)。从回调迁移到 Promise 的关键一步是「包装」:把回调式 API 包进 new Promise 里 resolve/reject。
示例
Promise 的 then 回调是异步执行的,运行沙盒只能看到同步部分。先看同步可观察的事实:
// 执行器是同步执行的
const p = new Promise((resolve) => {
console.log('执行器:同步立即执行');
resolve(42);
});
console.log('创建之后:Promise 已存在,状态为 pending 或已落定');
// Promise.resolve 立即成功,但 then 回调仍被排到微任务
Promise.resolve('立刻成功').then((v) => {
// 这行在浏览器控制台能看到,但在本沙盒的同步窗口之后执行
console.log('then 回调:', v);
});
console.log('同步代码总是先于 then 回调输出');
// 链式中的值传递(同步部分):每个 then 返回新 Promise
const chain = Promise.resolve(1)
.then((x) => x + 1)
.then((x) => x * 10);
console.log('链式返回的还是 Promise:', chain instanceof Promise);
完整的异步用法示意(在浏览器控制台或 Node 中运行):
function delay(ms, value) {
return new Promise((resolve) => setTimeout(() => resolve(value), ms));
}
// 链式:把回调地狱拉平
delay(300, '用户')
.then((user) => {
console.log('拿到', user);
return delay(300, '订单'); // return 新 Promise,下一步等它
})
.then((orders) => console.log('拿到', orders))
.catch((err) => console.error('任一环出错都会到这:', err))
.finally(() => console.log('收尾:关掉加载动画'));
// 并发:三个请求并行,全部完成才继续
Promise.all([delay(100, 'a'), delay(200, 'b'), delay(150, 'c')]).then((results) => {
console.log(results); // ['a', 'b', 'c'],按传入顺序,约 200ms 出结果
});
// 容错并发:成败各自记录
Promise.allSettled([delay(100, 'ok'), Promise.reject(new Error('bad'))]).then((outcomes) => {
console.log(outcomes[0].status, outcomes[1].status); // 'fulfilled' 'rejected'
});
常见坑
- then 里忘记 return:.then(x => { fetch(next) }) 没 return,链条不再等待里面的 Promise,后续 then 拿到 undefined。想串联必须 return。
- 嵌套 then 退化成回调地狱:then 里面再 then 再 then,违背了链式设计的初衷——扁平地 return 下去即可。
- 以为 Promise 可以取消:Promise 落定后无法撤销,「取消请求」要用 AbortController(fetch 章节),Promise 本身没有这个能力。
- 吞掉 reject:链尾不写 catch,失败变成 unhandledrejection,静默丢失错误。每条链的终点都该有 catch 或 await 时的 try/catch。
小结
Promise 是一次性状态机:pending → fulfilled/rejected;then/catch/finally 消费,链式拉平嵌套;all/allSettled/race/any 管并发。下一章用 async/await 把它写得像同步代码。