手写Promise
先来捋一下我们使用正常promise的的场景,然后去实现MyPromise这个类相对应的功能。
1.实现核心逻辑 类,构造函数执行器,状态,resolve,reject函数改变状态,值,then方法定义状态改变后的回调等功能。
2.基础类 的基础上加入异步逻辑,就是说执行器函数里加入延时操作,异步任务后更改promise对象的状态
3.实现使用then方法多次被调用,添加多个处理的回调函数。
4.then方法的链式调用:
- .then方法返回一个promise对象,这个promise对象在then方法里面可以拿到then方法里面回调函数的返回值,然后把这个返回值作为新promise对象的返回值输出,在被链式调用的时候就可以被它的then方法的回调函数拿到
- .同时还要判断这个then方法回调函数的返回值是普通值还是promise对象,是promise对象的话还需要在返回值promise对象的then方法的回调里面 使用新promise的resolve或reject把值传出去。
5.判断第一个then方法的回调函数返回值不能是promise2本身,否则就会发生promise对象的循环调用,我测试了很久发现这个promise2对象的状态将永远为pedding,永远不会被改变!
6-1. 之前我们还没有在Mypromise的类当中去进行任何的错误处理。接下来回想一下在使用Promise时我们所有可能出现错误或者抛出异常的情况:
- 执行器函数:执行器当中的代码在执行中发生错误时,执行reject()函数。
- then方法:then方法的回调函数在执行过程中报错,在下一个then方法的错误回调中捕获。
6-2. then方法链式调用的时候,只处理了成功的状态,还要处理rejected状态和pedding状态。
7.then方法的参数变成可选参数:then方法可以不传参数,promise状态就会一直向后传递,直到传递给有回调函数的then方法。
8. Proise.all 特点 :
- 返回一个Promise
- 入参是数组 resolve的情况下出参也是数组 且结果顺序和调用顺序一致
- 所有的值或者promise都完成才能resolve 所有要计数
- 只要有一个为reject 返回的Promise便reject
9.Promise.resolve的方法实现:把给定的值 转化为promise对象 1.普通值,2.promise对象 直接返回。
写源码过程中烧脑的问题1:链式调用的时候,如果promise对象then方法的回调函数的返回值的是then方法返回的promise对象,那么这个promise对象的状态将永远为pedding,永远不会被改变! 所以需要判断then方法的回调函数返回值不能为then方法返回的promise对象。需要判断的这个操作。
下面上代码:
// 这里定义三个常量,分别是promise对象的三种状态 pedding, fulfilled , rejected
const PEDDING = "pedding";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise{
// promise对象是通过new Promise生成的一个实例化对象,
// 所以我们要写的MyPromise是一个类,它接收一个参数,这个参数是一个函数,这个函数被称为Promise的执行器,这个执行器立即执行
constructor(executor){
// 执行器执行的时候 接收两个参数resolve和reject,这两个参数也是两个函数,它们的作用是改变promise对象的状态并传值给then方法的回调函数
// resolve将promise对象的状态修改为fulfilled成功状态,reject将promise对象的状态修改为rejected失败状态
try{
executor( this.resolve,this.reject )
} catch(e){
this.reject(e.message)
}
}
// 定义promise对象的默认状态为pedding
status = PEDDING;
// 成功之后的值
value = undefined;
// 失败之后的值
reason = undefined;
successCallback = [];
failCallback = [];
// 定义resolve函数,它接收数据并把
resolve = (value)=>{
if(this.status!=PEDDING) return; //一旦状态确定就不可更改
this.status = FULFILLED;
this.value = value;
// 如果this.successCallback的长度大于0,说明在promise对象状态为pedding的时候,就已经在promise对象的then方法定义过状态改变的回调函数了,那就执行吧!
// 下面reject函数中同理
if(this.successCallback.length>0){
this.successCallback.forEach((Callback)=>{
Callback(this.value)
})
}
}
// 定义reject函数
reject = (reason)=>{
if(this.status!=PEDDING) return; //一旦状态确定就不可更改
this.status = REJECTED;
this.reason = reason;
if(this.failCallback.length>0){
this.failCallback.forEach((item)=>{
item(this.reason)
})
}
}
// then方法 接收两个函数作为参数,这两个函数会被定义为promise状态更改后需要执行的回调函数
then(successCallback,failCallback){
successCallback = successCallback ? successCallback : value=>value;
failCallback = failCallback ? failCallback : reason=> {throw reason};
// console.log(successCallback,failCallback)
const promise2 = new MyPromise((resolve,reject)=>{
// 当前状态为成功
if(this.status == FULFILLED){
setTimeout(()=>{
try{
let x = successCallback(this.value);
// console.log(x)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e.message)
}
},0);
}else if(this.status == REJECTED){
// 当前状态为失败
setTimeout(()=>{
try{
let x = failCallback(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0);
}else{
// 当前状态为pedding(进行中!)
// 往this.successCallback中添加回调函数可以实现使用then方法多次被调用
this.successCallback.push(
()=>{
setTimeout(()=>{
try{
let x = successCallback(this.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e.message)
}
},0);
}
);
this.failCallback.push(
()=>{
setTimeout(()=>{
try{
let x = failCallback(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e.message)
}
},0);
}
);
}
})
return promise2;
}
// catch方法就是then方法的一个别名,它专门用来捕获错误,看起来只传递了一个回调函数,实际上把它的回调函数传给了then方法的第二个参数
catch(callBack){
return this.then(undefined,callBack);
}
// finally方法的作用:
// 1.是无论当前promise对象最终成功还是失败都会调用finally方法,并且它还可以链式调用then方法来拿到当前promise对象的值
// 2.finally方法的返回值如果是promise2对象,那么它链式调用的then方法会等待它返回值的promise2对象执行完成,但它链式调用的then方法接收到的值是当前promise对象的值
finally(Callback){
// return MyPromise.resolve( Callback() ).then()
return this.then((value)=>{
let end = MyPromise.resolve( Callback() ).then(()=>{return value});
// console.log(end);
return end;
},reason=> {
return MyPromise.resolve( Callback() ).then(()=>{throw reason})
});
}
static all(array){
let result = [];
let num = 0; //计数器
return new MyPromise((resolve,reject)=>{
function addResult(index,value){
num++;
result[index] = value;
// 当计数器等于传入数组的长度时,表明传入的所有promise对象或值都已经执行完毕,可以将当前promise对象状态改变为成功,并返回所有值。
if(num == array.length){
resolve(result);
}
}
for(let i=0;i<array.length;i++){
if(array[i] instanceof MyPromise){
// 传入的数组元素执行成功就往result里面添加,有一个失败则将当前promise对象状态改变为失败,并返回失败原因。
array[i].then((value)=>{addResult(i,value)} , (reason)=>{ reject(reason) })
}else{
addResult(i,array[i])
}
}
})
}
static resolve(x){
if( x instanceof MyPromise ){
return x;
}else{
return new MyPromise((resolve,reject)=>{
resolve(x);
})
}
}
}
// 封装一个方法判断第一个then方法的回调函数返回值是普通值还是promise对象,拿到最终值 改变promise2的状态并把值传出去
function resolvePromise(promise2,x,resolve,reject){
if( x == promise2 ) {
// 判断第一个then方法的回调函数返回值不能是promise2本身,否则就会发生promise对象的循环调用,我测试了很久发现这个promise2对象的状态将永远为pedding,永远不会被改变!
// 也就是链式调用在此步断掉了,所以必须做这个判断
return reject( new TypeError("chaining circle are detected for promise #<Promise>") )
}
if( x instanceof MyPromise ){
// x是promise
x.then( (value)=>resolve(value),(reason)=>reject(reason) )
}else{
// x是普通值
resolve(x);
}
}
module.exports = MyPromise;