前言
在 JavaScript 的世界里,异步操作无处不在。从早期的“回调地狱(Callback Hell)”到现在的线性逻辑,Promise 的出现彻底改变了我们编写异步代码的方式。它不仅是一个构造函数,更是一种流程控制的哲学。
一、 为什么需要 Promise?(背景与痛点)
- 告别回调地狱:回调函数里面嵌套过多回调函数,导致的回调地狱问题,而Promise通过回调函数延迟绑定和回调函数返回值穿透的技术,解决了回调地狱问题。
- 线性逻辑:异步函数过多时,导致的代码逻辑不连贯,Promise封装了异步代码,让处理流程变得线性。
二、 Promise 的灵魂:三种状态与两个特性
Promise 就像是一个状态机,它的核心在于状态的不可逆性。
1. 三种状态
- Pending(进行中) :初始状态,异步操作尚未完成。
- Fulfilled(已成功) :操作成功完成,通常通过调用
resolve()触发。 - Rejected(已失败) :操作失败,通常通过调用
reject()触发。
2. 核心特性
- 不可逆性:状态只能从
Pending变为Fulfilled或Rejected。一旦变为 Resolved(已定型) ,状态就不会再变。 - 结果持久:如果你在状态改变之后再添加回调,回调函数依然会立即得到结果。
三、 Promise 的优缺点辩证
-
优点:代码结构化、支持链式调用、错误处理统一。
-
缺点:
- 无法中途取消:一旦
new出来就会立即执行。 - 错误黑盒:如果不设置回调,内部抛出的错误不会反应到外部。
- 状态模糊:处于
Pending时,无法得知进度(是刚开始还是快结束了)。
- 无法中途取消:一旦
四、 核心用法全拆解
1. 构造函数是“同步”执行的!
注意:在实例化 Promise 时传入的函数会立即执行。
new Promise(function(resolve, reject) {
console.log("我会立即执行"); // 同步执行
resolve("成功");
});
console.log("我在 Promise 后面");
// 输出顺序:我会立即执行 -> 我在 Promise 后面
2. resolve 与 reject 的博弈
resolve和reject是传入function的两个参数,这两个参数也是函数。
- 当我们在
function中调用resolve方法时,Promise实例对象的状态就变为fulfilled(操作成功)。当Promise状态变为fulfilled时,就会执行Promise实例对象里面的then方法方法里面的onfulfiled方法 - 当我们在
function中调用reject方法时,Promise实例对象的状态就变为rejected(操作失败)。当Promise状态变为reject,就会执行Promise实例对象里面的then方法里面的onrejected方法,或者执行Promise实例对象里面的catch方法
注意:当在
function里面调用resolve和reject时,先调用的那个函数会执行,后调用的不会执行了,因为Promise实例对象的状态一旦改变就不会再改变
var p = new Promise(function (resolve, reject) {
var timer = setTimeout(function () {
console.log("执行操作1");
resolve("这是数据1");
console.log("执行操作2");
reject("这是数据2");
}, 1000);
});
p.then(
function (data) {
console.log(data);
console.log("这是成功操作");
},
function (data) {
console.log(data);
console.log("这是失败操作");
}
);
//执行操作1 执行操作2 这是数据1 这是成功操作
五、 then 与 catch 的执行潜规则
-
then里面包含两个参数:
onfulfilled和onrejected,这两个参数也是方法onfulfilled:当Promise实例对象的状态为fulfilled会执行这个方法onrejected:当Promise实例对象的状态为rejected会执行这个方法
// resolve例子 var p = new Promise(function (resolve, reject) { var timer = setTimeout(function () { console.log("执行操作1"); resolve("这是数据1"); }, 1000); }); p.then( function (data) { console.log(data); console.log("这是成功操作"); }, function (data) { console.log(data); console.log("这是失败操作"); } ); //执行操作1 这是数据1 这是成功操作// rejected例子 var p = new Promise(function (resolve, reject) { var timer = setTimeout(function () { console.log('执行操作2'); reject('这是数据2'); }, 1000); }); p.then(function (data) { console.log(data); console.log('这是成功操作'); }, function (data) { console.log(data); console.log('这是失败操作'); }); //执行操作2 这是数据2 这是失败操作 -
catch方法其实和then方法里面第二个参数一样,当Promise实例对象的状态为rejected会执行这个方法。 -
注意:如果
then方法里面onrejected方法和cathch方法同时存在,则分为两种情况:catch方法紧跟在then方法后面:只执行than方法里面的onrejected方法catch方法单独写在外面:两个都执行,先执行then方法里面onrejected方法,再执行catch方法
var p = new Promise(function (resolve, reject) {
var flag = false;
if(flag){
resolve('这是数据2');
}else{
reject('这是数据2');
}
});
p.then(function(data){
console.log(data);
console.log('这是成功操作');
},function(data){
console.log(data)
console.log('这是失败操作1')
}).catch(function(reason){
console.log(reason);
console.log('这是失败操作2');
});
//这是数据2 这是失败操作1
var p = new Promise(function (resolve, reject) {
var flag = false;
if(flag){
resolve('这是数据2');
}else{
reject('这是数据2');
}
});
p.then(function(data){
console.log(data);
console.log('这是成功操作');
},function(data){
console.log(data)
console.log('这是失败操作1')
})
p.catch(function(reason){
console.log(reason);
console.log('这是失败操作2');
});
//这是数据2 这是失败操作1 这是数据2 这是失败操作 2
六、 实战:看代码说结果
Promise.resolve(1)
.then((res) => {
console.log(res);
return 2;
})
.then((res) => {
console.log(res);
})
.then((res) => {
console.log(res);
return Promise.resolve(3);
})
.then((res) => {
console.log(res);
});
输出结果: 1, 2, undefined, 3
解析:
- 第一个
then接收1,返回2(这个2会被包装成Promise.resolve(2))。 - 第二个
then接收2,打印2。注意:这里没写 return,默认返回undefined。 - 第三个
then接收undefined,打印undefined,返回Promise.resolve(3)。 - 第四个
then接收3,打印3。