别让平淡的生活,毁了对未来的向往❤
1.promise的基本结构
promise必须接受一个函数( handle ) 作为参数,这个函数有两个参数:resolve,reject,且这两个参数本身也是函数。
let promise = new Promise((resolve, reject) => {
resolve('success'); //reject('fail')
});
promise.then((res)=>{
console.log(res); // 输出:success
},(err)=>{
console.log(err); // 上面如果执行reject('fail'),这里就输出:fail
});
2.promise的状态和值
(1)promise有三个状态
-
Pending
( 进行中 )
Fulfilled
( 已成功 )Rejected
( 已失败 )
状态只能由 **Pending--> Fulfilled** 或着 **Pending --> Rejected** 两种状态变更,变更之后状态**锁定**了,就不会再变更了。
(2)promise返回两种状态
resolve
(成功 ):将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)reject
(失败):将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败)
resolve 和 reject 函数都可以传入任意类型的值作为实参,表示 Promise 对象成功(Fulfilled)和失败(Rejected)的值。
(3)promise的值
Promise的值是指状态改变时传递给回调函数的值。
promise的参数handle函数,包含 resolve 和 reject 两个参数,它们是两个函数,可以用于改变 Promise 的状态和传入 Promise 的值。
(a) . resolve 传入的 ‘success’ ,就是 promise 的值。
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
}, 1000)
});
(b). promise的 then方法返回一个promise。
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
promise2 = promise1.then(res => {
return new Promise((resolve,reject)=>{
resolve('hello world');
})
})
promise2.then(res => {
console.log(res) //1秒后打印出:hello world
});
(4)实现一个Promise类
执行状态:
status
;可以有 pending,resolved,rejected三个状态。执行结果:
value
;返回成功时,传递进来的值由value保存。失败原因:
reason
;返回失败时,失败的原因由reason保存。两个方法:
resolve方法,reject方法
; 来进行状态值得改变及成功或失败原因的保存。一个方法:
then
方法;根据不同状态,执行不同的函数,返回不同的结果。错误处理:
try-catch
;能够捕获错误,并且处理错误。
//创建一个Promise类
class Promise {
constructor(executor) {
this.status = 'pending'; // 执行状态: 初始默认状态为pending(只有状态为pending才能转换状态)
this.value = undefined; // 执行结果:默认赋值为undefined
this.reason = undefined; // 失败原因:默认赋值为undefined
let resolve = (value) => {
if (this.status === 'pending') {
this.value = value; //将传递进来的的值赋给value保存
this.status = 'resolved'; //将状态设置成resolved
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason; //将传递进来的失败原因赋给reason保存
this.status = 'rejected'; //将状态设置成rejected
}
}
// 当代码出现错误的情况下,我们需要能够捕获错误,并且处理错误
try{
executor(resolve, reject); //默认执行executor
}catch(e){
reject(e);//如果发生错误,将错误放入reject中
}
}
then(onFulfilled, onRejected) { //等同于es5的Promise.prototype.then 当调用then的时候,根据状态,来执行不同的函数
if (this.status === 'resolved') {
onFulfilled(this.value); //如果状态是resolved,执行成功的resolve,并将成功后的值传递过去
}
if (this.status === 'rejected') {
onRejected(this.reason); //如果状态是rejected,执行失败的reject,并将失败原因传递过去
}
}
}
以上实现会有一个问题,如果promise内部使用setTimeout去调用回调函数时,我们会发现上面的代码在控制台完全没有反应,实际上是因为我们没有处理这种延迟调用的情况。
(5)setTimeout 调用问题
成功函数数组:
successStore[ ]
;成功resolve状态时,依次调用数组中函数。失败函数数组:
failStore[ ]
;失败reject状态时,依次调用数组中函数。then方法
:新增pending状态的判断,成功的函数存放到successStore数组里,失败的函数存放到fillStore数组中。
let promise = new Promise((resolve, reject) => {
setTimeout(() =>{
resolve('success'); //reject('fail')
},1000);
});
promise.then((res)=>{
console.log(res);
},(err)=>{
console.log(err);
});
解决方法:【在上述代码基础上完善,如下代码】
class Promise {
constructor(executor) {
...此处略去部分代码
this.successStore = []; //定义一个存放成功函数的数组
this.failStore = []; //定义一个存放失败函数的数组
let resolve = (value) => {
if (this.status === 'pending') {
...此处略去部分代码
this.successStore.forEach(fn => fn()); //依次执行数组中的成功函数
}
}
let reject = (reason) => {
if (this.status === 'pending') {
...此处略去部分代码
this.failStore.forEach(fn => fn()) //依次执行数组中的失败函数
}
}
...此处略去部分代码
}
then(onFulfilled, onRejected) { //等同于es5的Promise.prototype.then 当调用then的时候,根据状态,来执行不同的函数
...此处略去部分代码
if (this.status === 'pending') { //此处增加一种状态判断
this.successStore.push(() => { //当状态为pending时将成功的函数存放到数组里
onFulfilled(this.value);
})
this.failStore.push(() => { //当状态为pending时将失败的函数存放到数组中
onRejected(this.reason);
})
}
}
}
3.promise的then方法详解
Promise 对象的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected);
(1)参数可选
如果参数不会函数,则忽略
如果参数为函数:onFulfilled在promise状态变为成功时调用,onRejected在promise状态变为失败时调用。
(2)多次调用
当 promise 成功状态时,所有 onFulfilled 需按照其注册顺序依次回调。
当 promise 失败状态时,所有 onRejected 需按照其注册顺序依次回调。
(3)返回
- then 方法必须返回一个新的 promise 对象
promise2 = promise1.then(onFulfilled, onRejected);
- then 方法可以链式调用
promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2);
(4)链式调用
a. 在then方法的回调里返回一个普通值。
无论是成功还是失败的回调,都会进入到下一个then()的成功态里。
let promise = new Promise((resolve, reject) => {
resolve('success');
});
//返回一个普通值
promise.then(res => {
console.log(res); //success
return "hello world";
}, err => {
console.log(err);
}).then(res => {
console.log(res); //hello world
}, err => {
console.log(err);
})
b.在then方法的回调里返回一个新的Promise。
let promise = new Promise((resolve, reject) => {
resolve();
});
//返回一个新的Promise
promise.then((res)=>{
return new Promise((resolve,reject)=>{
resolve('hello world');
})
},(err)=>{
console.log(err);
}).then((res)=>{
console.log(res); //hello world
},(err)=>{
console.log(err);
})
4.promise的其他方法的实现
(1)promise.catch 方法
相当于调用 then 方法, 但只传入 Rejected 状态的回调函数
catch (onRejected) {
return this.then(undefined, onRejected);
}
(2)promise.reject 方法
static resolve (value) {
return new Promise(resolve => reject(value));
}
(3)promise.resolve方法
static resolve (value) {
return new Promise(resolve => resolve(value));
}
(4)promise.all 方法
【经典的手写promise.all 的面试题】
返回一个Promise对象
遍历传入的list集合; Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组
数组参数如果不是Promise实例,先调用Promise.resolve
所有状态都变成 fulfilled 时返回的Promise状态就变成 fulfilled
有一个被rejected时返回的Promise 状态就变成 rejected
static all(list){
return new Promise((resolve, reject) => {
let values = [];
let count = 0;
for (let [i, p] of list.entries()) {
this.resolve(p).then(res => {
values[i] = res;
count++;
// 所有状态都变成fulfilled时,就resolve
if (count === list.length) {
resolve(values);
}
}, err => {
// 有一个被rejected时,就rejected
reject(err);
})
}
})
}
(5)promise.race 方法
- 只要有一个实例率先改变状态,新的Promise的状态就跟着改变
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
//有一个状态改变时,就rejected或resolve
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
(6)promise.finally 方法
- finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
finally (cb) {
return this.then(
value => Promise.resolve(cb()).then(() => value),
reason => Promise.resolve(cb()).then(() => { throw reason })
);
};
5.promise其他方法的使用
(1)Promise.all( )
传入的参数是一个数组
若全部都是成功的回调,则执行会输出返回值组成的数组 【传入p1,p2成功的回调】
//成功
let p1 = new Promise((resolve,reject) => {
let value = {
code:0,
msg:'one success'
};
resolve(value);
});
//成功
let p2 = new Promise((resolve,reject) => {
let value = {
code:1,
msg:'two success'
};
resolve(value);
});
//失败
let p3 = new Promise((resolve,reject) => {
let reason = 'all error';
reject(reason);
});
Promise.all([p1,p2]).then((val) => {
console.log(val);
}).catch(err=>{
console.log(err);
});
输出结果:
- 若存在失败的回调,则直接返回失败的信息。【加入P3失败的回调】
Promise.all([p1,p2,p3]).then(val => {
console.log(val);
}).catch(err=>{
console.log(err);
});
输出结果:
【适合场景】
如上p1,p2,p3的三个情况,只有全部是成功的回调promise才会成功状态,若有一条失败则promise返回状态。
- 比如现在有一个表单页,里面有若干多个表单项,只有在所有表单都通过校验才允许用户进行下一步操作。
- 每一个业务环节需要顺序执行,执行顺序不可变,状态不可逆,上一个执行结果可能需要作为下一步的输入。
(2)Promise.race( )
传入的参数是一个数组
会返回最先出结果的那个元素的状态
//成功回调,直接返回
let p1 = new Promise((resolve,reject) => {
let value = {
code:0,
msg:'one success'
};
resolve(value);
});
//成功状态,延迟1秒返回
let p2 = new Promise((resolve,reject) => {
let value = {
code:1,
msg:'two success'
};
setTimeout(()=>{
resolve(value);
},1000);
});
//失败状态,延迟0秒返回
let p3 = new Promise((resolve,reject) => {
let reason = 'race error'
setTimeout(()=>{
reject(reason);
},0);
});
Promise.race([p1,p2,p3]).then(val => {
console.log(val);
}).catch(err=>{
console.log(err);
})
输出结果:
为什么不输出延迟0秒的p3 ‘race error’ 呢,因为这个牵扯到事件执行机制,同步任务先执行,异步setTimeout任务挂起,同步任务执行结束之后再执行异步。所以p3没有p1返回的快。
【适用场景】
如上p1,p2,p3,不管他们的状态是成功还是失败,只是返回最先执行完的那个。所以promise.race可以用于快捷地测试接口反应速度。