深入理解 JavaScript Promise:从构造到状态流转
在现代 JavaScript 开发中,Promise 已成为处理异步操作的标准工具。但很多人只是“会用”,却不理解它背后的运行机制。本文将带你从零开始,深入剖析 Promise 的构造方式、内部状态变化规则,以及 .then() 和 .catch() 是如何协同工作的。
一、Promise 是如何构造的?
Promise 是一个内置的构造函数,通过 new Promise() 创建实例:
const myPromise = new Promise((resolve, reject) => {
// executor(执行器)函数
});
构造函数接收一个参数:executor 函数
-
这个函数会立即同步执行(不是异步!)。
-
Promise有三种状态
-
- pending(待定)
-
- fulfilled(已兑现)
-
- rejected(已拒绝)
-
Promise两个内部属性
-
- state
-
- result
-
Promise接收两个参数:
-
resolve(value):用于将 Promise state变为 fulfilled(成功)、result:value -
reject(error):用于将 Promise state变为 rejected(失败)、result:error
-
注意:即使你没有调用
resolve或reject,Promise 也会一直处于pending状态,永远不会完成。
示例:基本构造
console.log(1);
//Promise 异步任务同步化
//许诺 Promise 包含一个耗时性的任务
const p=new Promise((resolve)=>{
setTimeout(function() {
console.log(2);
resolve();
}, 3000);
})
p.then(()=>{
console.log(3);
})
console.log(4);
输出顺序:
1和4作为同步任务先执行
2在3秒后输出
当异步任务结束成功后.then处理函数输出3
这说明:executor 是同步执行的,但其中的异步逻辑(如 setTimeout)会被放入任务队列。
二、Promise 状态的不可逆性
状态一旦改变,就不可逆转
const p = new Promise((resolve, reject) => {
resolve('第一次 resolve');
resolve('第二次 resolve'); // 无效!
reject('尝试 reject'); // 也无效!
});
p.then(console.log); // 只输出 "第一次 resolve"
executor 只能调用一个
resolve或一个reject。任何状态的更改都是最终的。 所有其他的再对resolve和reject的调用都会被忽略:
三、.then():监听状态变化的桥梁
.then() 是 Promise 最核心的方法,用于注册成功和失败的回调函数。
语法
promise.then(
onFulfilled, // 当状态变为 fulfilled 时调用
onRejected // 当状态变为 rejected 时调用(可选)
);
每次只根据异步处理结果运行一个函数
关键特性
.then()总是返回一个新的 Promise,支持链式调用。- 如果
onFulfilled返回一个值,新 Promise 会被resolve该值。 - 如果
onFulfilled抛出异常,新 Promise 会被reject。
示例:成功与失败处理
const p = new Promise((resolve, reject) => {
Math.random() > 0.5 ? resolve('OK') : reject(new Error('NO'));
});
p.then(
result => console.log(' 成功:', result),
error => console.log(' 失败:', error.message)
);
提示:虽然
.then()支持两个参数,但更推荐使用.catch()统一处理错误。
四、.catch():专为错误设计的处理器
.catch(onRejected) 是 .then(null, onRejected) 的语法糖。
promise
.then(result => { /* 处理成功 */ })
.catch(error => { /* 处理失败 */ });
为什么推荐用 .catch()?
- 统一错误捕获:链式调用中,任何一个环节抛出异常,都会被后续的
.catch()捕获。 - 避免遗漏错误处理:如果只用
.then()的第二个参数,无法捕获.then()回调内部的错误。
对比示例
// 不推荐:无法捕获 then 内部的错误
promise.then(
() => { throw new Error('Oops!'); },
err => console.log('这里不会执行!')
);
// 推荐:能捕获所有前面的错误
promise
.then(() => { throw new Error('Oops!'); })
.catch(err => console.log('捕获到了:', err.message));
.catch其实是.then处理错误的一种简写方式.catch(f)调用是.then(null,f)的一种模拟
🔗 五、链式调用与状态传递
由于 .then() 和 .catch() 都返回新的 Promise,我们可以构建清晰的异步流程:
fetchUser()
.then(user => fetchPosts(user.id))
.then(posts => render(posts))
.catch(err => showError(err));
在这个链条中:
- 每一步的返回值会作为下一步的输入;
- 任何一步出错,都会跳转到最近的
.catch()。
总结:Promise 的核心心智模型
- 构造即执行:
new Promise(executor)会立即运行 executor。 - 状态单向不可逆:
pending → fulfilled或pending → rejected,仅一次。 .then()是观察者:根据状态决定调用哪个回调。.catch()是安全网:集中处理整个链路的异常。- 链式调用靠返回新 Promise:实现异步流程的线性表达。