Promise 对象代表一个异步操作的最终完成或失败,以及其结果值。它的核心在于一个简单的状态模型——理解这个模型,就理解了 Promise 的行为规则。
三种状态
一个 Promise 实例在任意时刻必然处于以下三种状态之一:
- Pending(待定):初始状态,异步操作尚未完成,既没有成功也没有失败。
- Fulfilled(已完成):异步操作成功完成,Promise 拥有了一个不可变的结果值。
- Rejected(已拒绝):异步操作失败,Promise 拥有了一个不可变的失败原因(通常是一个 Error 对象)。
可以用一个日常比喻来理解:Promise 像是一张外卖订单。刚下单时是“待定”,厨师开始做;当快递员取到餐点,“已完成”(你可以拿结果值——餐点);如果餐馆打来电话说卖完了,“已拒绝”(你会得到一个理由——卖完了)。
// 创建后处于 pending 状态
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('数据'); // 转为 fulfilled
} else {
reject(new Error('网络错误')); // 转为 rejected
}
}, 1000);
});
console.log(promise); // Promise {<pending>}
状态不可逆:一旦确定,永远不会改变
这是 Promise 最基本、最重要的可靠性保证:Promise 的状态只能从 pending 变为 fulfilled 或 rejected,一旦变更,就永久凝固,不能再变回 pending,也不能切换到另一个终态。 这个结果值和状态会被一直保留,即使你在状态确定之后很久才去添加 .then() 或 .catch(),依然能拿到当初的结果。
const promise = new Promise((resolve, reject) => {
resolve('第一次'); // promise 变为 fulfilled,值为 '第一次'
reject(new Error('第二次')); // 这行代码无效,被完全忽略
resolve('第三次'); // 同样无效
});
promise.then(
value => console.log(value), // 输出 '第一次'
error => console.log(error) // 不会执行
);
上面的代码中,当 resolve('第一次') 执行后,Promise 已经从 pending 转变为了 fulfilled,后续任何 reject 或 resolve 调用都会被引擎静默忽略。这种“一次性”特性避免了回调地狱中常见的多次调用、提前或延迟调用导致的状态混乱问题。
实际开发中的价值:
- 你可以安全地多次订阅同一个 Promise,它不会多次触发异步操作,也不会因为时间差而丢失结果。
- 无论你在路由跳转、组件卸载、或超时取消时如何操作,已经 settled(确定状态)的 Promise 不会受到干扰,避免了“竞态”中的状态污染。
- 这种确定性使得 Promise 可以放心地作为异步结果的标准化容器,在组合、链式调用、并发控制中保持行为一致。
与“取消”的区别
需注意,Promise 没有内置的取消机制。一旦异步操作启动,Promise 的状态终究会变为 fulfilled 或 rejected,你不能中途将其重置为 pending。如果要实现“取消”效果,常见的做法是结合一个封装好的标志位或使用第三方库(如 Axios 的 CancelToken),让异步操作的完成回调根据标志判断是否忽略结果,而不是去改变 Promise 的状态本身。
小结
Promise 的三种状态和不可逆特性共同构成了它作为异步基础的可靠性:一旦你得到一个 Promise,你就可以信赖它的结果永远不会变。这一简单的保证,让复杂的异步流程组合成为可能,也为 async/await 提供了坚实的底层基础。理解这一点,是写出正确异步代码的起点。