Promise 是现代 JavaScript 异步编程的核心机制。它彻底改变了我们处理异步操作的方式,让代码从“回调地狱”中解放出来,变得更加清晰、可维护。
本文将带你全面、深入地理解 Promise,从基本概念到底层机制,再到实际应用。
一、什么是 Promise?
✅ 核心定义
Promise 是一个代表异步操作最终完成或失败的“承诺”对象。
- 它是一个容器,保存着某个未来才会结束的事件(通常是异步操作)的结果;
- 它提供统一的 API,让各种异步操作可以用相同的方式处理;
- 它比传统的回调函数和事件机制更合理、更强大。
✅ 类比理解
想象你去餐厅点餐:
- 回调函数:你坐在桌边,服务员说“做好了叫你”,但你得一直等,不能做别的事;
- Promise:你拿到一个取餐号(Promise 对象),可以去逛逛,等叫号时再去取餐;
- async/await:你对取餐号说“等叫到你,再继续下一步”,代码看起来像同步一样。
二、Promise 的三大状态
| 状态 | 说明 | 是否可变 |
|---|---|---|
| pending(进行中) | 初始状态,既未成功也未失败 | 可变 |
| fulfilled(已成功) | 操作成功完成 | ❌ 不可变 |
| rejected(已失败) | 操作失败 | ❌ 不可变 |
✅ 状态流转图
resolve()
pending ----------> fulfilled
|
| reject()
↓
rejected
🔑 关键特性:
- 状态一旦从
pending变为fulfilled或rejected,就永久凝固,无法再改变;- 这就是“承诺”的含义:一旦承诺兑现或失败,就无法反悔。
三、Promise 的基本用法
✅ 创建 Promise 实例
const myPromise = new Promise((resolve, reject) => {
// executor 函数:立即执行
console.log('Promise constructor running...'); // 立即打印
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('Success!'); // 变为 fulfilled
} else {
reject(new Error('Failed!')); // 变为 rejected
}
}, 1000);
});
📌 注意:Promise 构造函数内的代码是同步立即执行的,但 resolve 和 reject 通常是异步调用。
✅ 使用 .then() 和 .catch()
myPromise
.then(result => {
console.log('成功:', result);
return result.toUpperCase(); // 可链式返回新值
})
.then(upper => {
console.log('大写:', upper);
})
.catch(error => {
console.error('失败:', error.message);
})
.finally(() => {
console.log('无论成功失败,都会执行');
});
.then():处理fulfilled状态,接收resolve的值;.catch():处理rejected状态,接收reject的原因;.finally():无论成功失败都会执行,常用于清理资源。
四、Promise 的核心特点
✅ 1. 状态不受外界影响
只有 resolve() 和 reject() 能改变 Promise 的状态,外部代码无法干预。
const p = new Promise(resolve => {
setTimeout(() => resolve('Done'), 1000);
});
// 无法从外部改变状态
p.status = 'rejected'; // 无效!状态仍由 resolve 决定
✅ 2. 状态一旦改变,便不可逆
const p = new Promise((resolve, reject) => {
resolve('First');
reject('Second'); // 无效!状态已变为 fulfilled
});
p.then(console.log); // 输出: First
✅ 3. 可在任意时刻添加回调
即使 Promise 已经完成,你仍然可以添加 .then() 并立即得到结果。
const p = Promise.resolve('Cached result');
// 1秒后添加回调
setTimeout(() => {
p.then(console.log); // 立即输出: Cached result
}, 1000);
💡 这与事件(Event)不同:错过事件就无法监听到结果。
五、Promise 的链式调用(Chaining)
✅ 返回值决定下一个 .then
.then()返回一个新的 Promise;- 返回值决定下一个
.then的行为:
| 返回值 | 下一个 .then 接收 |
|---|---|
| 普通值 | 该值 |
Promise | 该 Promise 的 resolve 值 |
| 抛出错误 | 被 .catch() 捕获 |
✅ 示例:链式处理
fetch('/api/user')
.then(response => response.json())
.then(user => fetch(`/api/posts?userId=${user.id}`))
.then(postsResponse => postsResponse.json())
.then(posts => console.log('Posts:', posts))
.catch(err => console.error('Error:', err));
六、Promise 的静态方法
| 方法 | 说明 |
|---|---|
Promise.resolve(value) | 返回一个 resolved 的 Promise |
Promise.reject(reason) | 返回一个 rejected 的 Promise |
Promise.all(iterable) | 所有 Promise 都 fulfilled 才 resolve,任一 reject 则 reject |
Promise.race(iterable) | 第一个完成的 Promise 决定结果 |
Promise.allSettled(iterable) | 等待所有 Promise 结束,无论成功失败 |
Promise.any(iterable) | 第一个 fulfilled 的 Promise 决定结果,全部 reject 才 reject |
✅ 使用示例
// 并发请求,全部成功才继续
Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
]).then(results => {
console.log('All data loaded');
});
// 谁快用谁
Promise.race([
fetch('/api/fast'),
fetch('/api/slow')
]).then(first => {
console.log('Fastest response:', first);
});
七、Promise 的缺点与局限
❌ 1. 无法取消
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.onload = () => resolve(xhr.responseText);
xhr.send();
// 无法从外部取消请求
});
🔧 解决方案:使用
AbortController(现代浏览器)。
❌ 2. 错误处理不透明
new Promise(() => {
throw new Error('Silent error');
});
// 错误不会抛出到全局,除非有 .catch()
🔧 最佳实践:始终使用
.catch()或try/catch(在 async 函数中)。
❌ 3. 无法监听进度
Promise 只有 pending/fulfilled/rejected 三种状态,无法得知“已完成 50%”这样的中间状态。
🔧 解决方案:对于文件上传等场景,使用
ProgressEvent或自定义进度回调。
八、Promise 与事件循环
✅ .then() 回调是微任务
console.log('1');
Promise.resolve().then(() => console.log('2'));
console.log('3');
输出:1 → 3 → 2
Promise.then()回调被加入 microtask queue;- 在当前宏任务结束后,立即执行所有微任务。
九、总结:Promise 的核心要点
| 特性 | 说明 |
|---|---|
| 状态 | pending → fulfilled / rejected,不可逆 |
| 构造函数 | 立即执行,resolve/reject 控制状态 |
| 链式调用 | .then() 返回新 Promise,支持异步流程控制 |
| 错误处理 | .catch() 捕获链中任何 reject 或异常 |
| 任务类型 | .then() 回调属于 微任务 |
| 优点 | 避免回调地狱、统一 API、可组合性强 |
| 缺点 | 无法取消、错误静默、无法监听进度 |
💡 结语
“Promise 不是异步的终点,而是现代异步编程的起点。”
它为 async/await 奠定了基础,是理解 JavaScript 异步机制的必经之路。
掌握 Promise,你就能:
- 写出清晰的异步代码;
- 理解
fetch、axios等库的返回值; - 应对复杂的异步流程控制。