Promise自定义封装

1、promise的几个关键问题

  1. 如何改变promise的状态?

  1. resolve(value):如果当前是pending就会变为resolved

  1. reject(value):如果当前是pending就会变为rejected

  1. 抛出异常:如果当前是pending就会变为rejected

const p = new Promise((resolve, reject) => {
  //1.resolve
  // resolve("success");//pending  =>  fulfilled  (resolved)

  //2.reject
  // reject("error");//pending   =>  rejected

  //3.抛出异常
  throw "异常";//pending   =>  rejected
})
  1. 一个promise指定多个成功/失败回调函数,都会调用吗?

当peomise改变为对应状态时都会调用

  1. 改变promise状态和指定回调函数谁先谁后?

  1. 都有可能,正常情况下是指定回调在改变状态,因为大部分都是放的异步代码,所以请求发送还没完成,就已经执行then()指定回调了,而此时promise的状态还是pening,但也可以改状态再指定回调,注意指定then()回调并不是调用then()中的回调函数

const p = new Promise((resolve, reject) => {
	//异步代码
  setTimeout(() => {
    resolve("success");
  });
})

p.then(res => {
  console.log(res);
});
  1. 如何先改变状态再指定回调?

  1. 在执行器中直接调用resolve()/reject()

  1. 延迟更长时间才调用then()

  1. 什么时候才能得到数据

  1. 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据

  1. 如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据

  1. promise.then()返回的新的promise的结果状态是由什么决定的

  1. 简单表达:由then()指定的回调函数执行的结果决定的

  1. 详细表达:

  1. 如果抛出异常,新promise变为rejected,reason为抛出的异常

  1. 如果返回的是非promise的任意值,新pormise变为resolved,value为返回的值

  1. 如果返回的是另一个新promise,此promise的结果就会成为新的promise的结果

  1. promise如何串联多个操作任务?

  1. promise的then()返回一个新的promise,可以看成then()的链式调用

  1. 通过then()的链式调用串联多个同步/异步任务

  1. promise异常穿透

  1. 当使用promise的then链式调用时,可以在最后指定失败的回调

  1. 前面任何操作出了异常,都会传到最后失败的回调中处理

  1. 中断promise链?

  1. 当使用promise的then链式调用时,在中间中断,不在调用后面的回调函数

  1. 唯一办法:在回调函数中返回一个pending状态的promise对象

2、Promise的自定义封装

2.1、定义整体结构

首先来看一下原生Promise的基本结构

  let p = new Promise((resolve, reject) => {
		//resolve();//成功回调
		//reject();//失败回调
  })

  p.then(res => {

  }, err => {

  })

所以我们根据这个就先定义一个Promise构造函数

function MyPromise(executor) {

}

/* MyPromise.prototype */
MyPromise.prototype = {
  constructor: MyPromise,

  /*添加 then() */
  then(onResolved, onRejected) {

  }

}

then()是通过实例对象调用的,所以就把then定义在原型上面

2.2、resolve与reject结构搭建

首先从 new Promise((resolve, reject) => {}) 这里作为一个突破点。

  1. 这个执行器函数在内部是同步调用的,就说明只要new MyPromise就会立即执行执行器函数executor()

  1. 执行器函数本身也有两个参数resolvereject,通过原生就知道这两个参数都是用来调用成功/失败的函数,接下来可以申明resolvereject

  1. resolvereject都有实参

function MyPromise(executor) {
  //resolve
  const resolve = function (data) {

  }
  //reject
  const reject = function (data) {

  }
  //同步调用 executor()
  executor(resolve, reject);
}

注意:由于resolve和reject使用const/let定义的,没有变量提升,所以 executor(resolve, reject)要写在后面

2.2.1、resolve与reject代码实现

  1. resolve和reject只要被调用都可以修改promise的状态(promiseState

  1. 可以修改promise的结果值(promiseResult

/* 定义promise的三个状态 */
const PENDING = 'pending';      //待定
const FULFILLED = 'fulfilled';  //成功
const REJECTED = 'rejected';    //失败

function MyPromise(executor) {
  //添加属性    起始值
  this.PromiseState = PENDING;
  this.PromiseResult = undefined;

  //保存实例对象的值
  const _this = this;
  //resolve
  const resolve = function (data) {
    //1.修改对象的成功状态 (PromiseState);
    _this.PromiseState = FULFILLED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;
  }

  //reject
  const reject = function (data) {
    //1.修改对象的失败状态 (PromiseState);
    _this.PromiseState = REJECTED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;
  }

  //同步调用 executor()
  executor(resolve, reject);
}

由于promise有固定的三个状态,这里就设置成了常量(当然也可以不声明,看个人习惯)

不过要注意的是reject和reject都是用的函数声明,所以函数在外面调用的时候这里面的this会指向window/undefined,就要在外面申明一个变量保存一下这个值,当然如果改成箭头函数就不用考虑了,它会指向当前函数声明的作用域,不过这里我随大众,在构造函数里面就都用函数声明了

2.2.2、resolve与reject代码实现(throw抛出异常改变状态)

改变promise状态的三种方式:

  1. resolve(成功)

  1. reject(失败)

  1. 抛出异常 (失败)

如果不做抛出异常改变状态的处理,就直接报错,整个代码都不执行了

 let p = new MyPromise((resolve, reject) => {
   throw "err";
})
    
console.log(p);

再来看看原生的promise,就会把promise的状态改为失败

这里就可以利用try ...catch()来处理异常,这里是executor()函数执行器调用的,

所以函数执行器在哪调用,try ...catch()就在哪加

	
  try {
    //同步调用 executor()
    executor(resolve, reject);
  } catch (e) {
    //抛出异常就调用reject修改状态为失败
		//抛出的数据e就promise失败的结果值
    reject(e)
  }

2.2.3、Promise对象的状态只能修改一次

let p = new MyPromise((resolve, reject) => {
      resolve("OK");
      reject("Error");
})

很明显这是先修改为成功的之后,又修改为失败的了,但是promise的规则就是只允许修改一次,所以继续从resolve和reject下手

  1. 如果状态改过了,就不用去改

  1. 如果状态没改过,就去更改

  //resolve
  const resolve = function (data) {
    //判断状态
		//通过PromiseState是否为pending判断
    if (_this.PromiseState !== PENDING) return;

    //1.修改对象的成功状态 (PromiseState);
    _this.PromiseState = FULFILLED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;
  }

  //reject
  const reject = function (data) {
    //判断状态
    if (_this.PromiseState !== PENDING) return;

    //1.修改对象的失败状态 (PromiseState);
    _this.PromiseState = REJECTED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;
  }

2.3、then方法

2.3.1、then方法指定执行回调

then有两个参数,分别对应成功和失败状态的执行回调

  1. 成功就执行then的第一个参数

  1. 失败就执行then的第二个参数

  1. then的两个参数函数都有一个形参,而形参就是当前promise的结果值PromiseResult

MyPromise.prototype = {
  constructor: MyPromise,
  /*添加 then() */
  then(onResolved, onRejected) {
		
    //调用回调函数  通过当前promise实例对象的PromiseState判断
    if (this.PromiseState === FULFILLED) {
      onResolved(this.PromiseResult);
    } else if (this.PromiseState === REJECTED) {
      onRejected(this.PromiseResult);
    }
  }

}

2.3.2、异步任务执行回调

如果说在实例Promise的时候里面是异步代码会怎么样?

在真正开发中,一般都是异步请求,文件io等等,这里先用定时器模拟一下异步代码,定时器本身也是个异步

首先看一下原生的

let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("OK");
      }, 1000);
    })

    p.then(res => {
      console.log(res);
    }, err => {
      console.log(err);
    })

    console.log(p);

自己的:

状态和结果确实改了,但是没有执行then方法,也就没有打印结果,这是什么原因?

首先我们要知道的是,现在这个then()他是属于同步代码,js一运行代码从上往下走,then也就立马执行,而resolve("OK")需要等到1s之后才调用,而此时promise对象的状态还没有修改,then又执行了,所以也就没有进到then的if语句里面

  1. 改变状态之后再去调用then的参数函数

  1. 只有resolve/reject方法才改变状态

  1. 在resolve/reject方法里面去调用then的参数函数

通过这个几个问题继续扩展

  1. 在then里面去判断pending的状态

  1. 声明一个属性先保存一下当前的这个回调函数,暂时不让他执行

  1. 等异步结束之后在resolve/reject方法里面去执行保存之后的回调函数

function MyPromise(executor) {
  //添加属性    起始值
  this.PromiseState = PENDING;
  this.PromiseResult = undefined;

  //保存回调函数
  this.callBack = {};
  //保存实例对象的值
  const _this = this;

  //resolve
  const resolve = function (data) {
    //判断状态
    if (_this.PromiseState !== PENDING) return;

    //1.修改对象的成功状态 (PromiseState);
    _this.PromiseState = FULFILLED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;

    //调用成功的回调
		//如果onResolved存在
    if (_this.callBack.onResolved) {
      _this.callBack.onResolved(data);
    }
  }

  //reject
  const reject = function (data) {
    //判断状态
    if (_this.PromiseState !== PENDING) return;

    //1.修改对象的失败状态 (PromiseState);
    _this.PromiseState = REJECTED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;

    //调用失败的回调
    if (_this.callBack.onResolved) {
      _this.callBack.onRejected(data);
    }
  }

  try {
    //同步调用 executor()
    executor(resolve, reject);
  } catch (e) {
    //抛出异常就调用reject修改状态为失败
    //抛出的数据 e 就promise失败的结果值
    reject(e)
  }
}
MyPromise.prototype = {
  constructor: MyPromise,

  /*添加 then() */
  then(onResolved, onRejected) {
    //调用回调函数  通过当前promise实例对象的PromiseState判断

    if (this.PromiseState === FULFILLED) {
      onResolved(this.PromiseResult);
    } else if (this.PromiseState === REJECTED) {
      onRejected(this.PromiseResult);
    } else if (this.PromiseState === PENDING) {
      //保存回调函数
      this.callBack = {
        onResolved: onResolved,
        onRejected: onRejected
      }
    }
  }
}

2.3.3、链式调用

在Promise中,只要状态发生改变,下面的方法都会执行,也就是链式调用,而我们自己写的暂时不行,因为每一次调用then都会覆盖掉上一次调用的then的结果和状态,所以不能这样保存。多个保存就用数组

属性:this.callBack = {} 换成 this.callBacks = []

// this.callBack = {
//   onResolved: onResolved,
//   onRejected: onRejected
// }

//改成
this.callBacks.push({
     onResolved: onResolved,
     onRejected: onRejected
})

构造函数里面也改一下,数组遍历调用

const resolve = function (data) {
    //判断状态
    if (_this.PromiseState !== PENDING) return;

    //1.修改对象的成功状态 (PromiseState);
    _this.PromiseState = FULFILLED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;

    //调用成功的回调
    _this.callBacks.forEach(item => {
      item.onResolved(data);
    })
  }

  //reject
  const reject = function (data) {
    //判断状态
    if (_this.PromiseState !== PENDING) return;

    //1.修改对象的失败状态 (PromiseState);
    _this.PromiseState = REJECTED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;

    //调用失败的回调
    _this.callBacks.forEach(item => {
      item.onRejected(data);
    })
  }

2.3.4、同步任务下then()返回的结果

  1. then返回的是一个promise对象

  1. then()的形参函数没有返回值,那then返回的是一个结果为undefined并且状态为成功的promise对象

  1. then()的形参函数的返回值是非promise对象,那then返回结果是结果为这个形参的返回值并且状态为成功的promise对象

  1. then()的形参函数的返回值是promise对象,那这个promise对象的返回结果就是这个then的返回结果

  1. then()的形参函数抛出异常,那then返回的是一个结果为抛出异常的值并且状态为失败的promise对象

根据上面then的返回值特点,接下来在then中就可以用一个变量来接受这个回调函数的一个返回结果来判断

then(onResolved, onRejected) {
    //调用回调函数  通过当前promise实例对象的PromiseState判断
    return new MyPromise((resolve, reject) => {
      if (this.PromiseState === FULFILLED) {
				//抛出异常,没有异常
        try {
          //获取回调函数的执行结果
          let result = onResolved(this.PromiseResult);
          if (result instanceof MyPromise) {
            //如果是 MyPromise对象
						//这个MyPromise对象是成功就返回成功,失败就返回失败
            result.then(res => {
              resolve(res);
            }, err => {
              reject(err);
            })
          } else {
            //结果的对象状态为成功
            resolve(result);
          }
        } catch (e) {//捕获异常
          reject(e);
        }
      } else if (this.PromiseState === REJECTED) {
        onRejected(this.PromiseResult);
      } else if (this.PromiseState === PENDING) {
        //保存回调函数
        this.callBacks.push({
          onResolved: onResolved,
          onRejected: onRejected
        })
      }
    })
  }

注意:这是同步状态下,如果加了定时器,效果不会出来,不过后面会解决

2.3.5、异步任务下then()返回的结果

如果我们加个定时器,异步代码会怎样

let p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve("OK");
      }, 1000);
    })

    const result = p.then(res => {
      console.log(res);
    }, err => {
      console.log(err);
    })

    console.log(result);

打印出来发现promise的状态和值都没有发什么变化,这是什么原因

所以返回的这个promise对象是没有发生状态改变的promise对象,那怎么解决?

  then(onResolved, onRejected) {
    const _this = this;
    //调用回调函数  通过当前promise实例对象的PromiseState判断
    return new MyPromise((resolve, reject) => {
      if (this.PromiseState === FULFILLED) {
        try {
          //获取回调函数的执行结果
          let result = onResolved(this.PromiseResult);
          if (result instanceof MyPromise) {
            //如果是 MyPromise对象
            result.then(res => {
              resolve(res);
            }, err => {
              reject(err);
            })
          } else {
            //结果的对象状态为成功
            resolve(result);
          }
        } catch (e) {
          reject(e);
        }
      } else if (this.PromiseState === REJECTED) {
        onRejected(this.PromiseResult);
      } else if (this.PromiseState === PENDING) {
        //保存回调函数
        this.callBacks.push({
          onResolved: function () {
            //执行成功的回调函数
            //成功的结果就是当前promise实例对象的PromiseResult
            //获取回调函数的执行结果
            let result = onResolved(_this.PromiseResult);
            //根据返回的结果判断这个promise的返回状态和值
            if (result instanceof MyPromise) {
              //如果是 MyPromise对象
              result.then(res => {
                resolve(res);
              }, err => {
                reject(err);
              })
            } else {
              //结果的对象状态为成功
              resolve(result);
            }
          },
          onRejected: function () {
            //老规矩
            let result = onRejected(_this.PromiseResult);
            //根据返回的结果判断这个promise的返回状态和值
            if (result instanceof MyPromise) {
              //如果是 MyPromise对象
              result.then(res => {
                resolve(res);
              }, err => {
                reject(err);
              })
            } else {
              //结果的对象状态为成功
              resolve(result);
            }
          }
        })
      }
    })
  }

解释一下:

_this.callBacks.forEach(item => {
      item.onRejected();
})

就拿下面这串代码来说,我知道到这里肯定会不懂,这个数组在上面的resolve/reject中就遍历调用了

 //保存回调函数
this.callBacks.push({
	onResolved: function () {
		//执行成功的回调函数
		//成功的结果就是当前promise实例对象的PromiseResult
		//获取回调函数的执行结果
		let result = onResolved(_this.PromiseResult);
		//根据返回的结果判断这个promise的返回状态和值
		if (result instanceof MyPromise) {
			//如果是 MyPromise对象
			result.then(res => {
				resolve(res);
			}, err => {
				reject(err);
			})
		} else {
			//结果的对象状态为成功
			resolve(result);
		}
	},
	onRejected: function () {
		//老规矩
		let result = onRejected(_this.PromiseResult);
		//根据返回的结果判断这个promise的返回状态和值
		if (result instanceof MyPromise) {
			//如果是 MyPromise对象
			result.then(res => {
				resolve(res);
			}, err => {
				reject(err);
			})
		} else {
			//结果的对象状态为成功
			resolve(result);
		}
	}
})

而只要异步代码一执行完毕就会改变状态,只要一改变状态就会执行resolve/reject,只要一执行resolve/reject,就会遍历数组执行数组里面的函数。

我们先来理清一下,onResolved是then()中的第一个参数作为回调函数,只要promise成功就会执行这个函数,之后就获取这个回调函数的执行结果,onResolved(_this.PromiseResult) 就是当前调用then的promise,而他里面的参数就是这个promise的结果值(这是在另一个函数里面所以要在外面通过const _this = this 保存一下this值,不能直接获取this),然后又是老规矩,如果不是MyPromise对象,结果的对象状态就是成功,如果是MyPromise对象,那这个MyPromise对象成功,我就resolve,如果失败,我就reject,其实这里就是把传进来的MyPromise对象的结果返回给then()中新MyPromise,保持一致

还有一点要注意,在控制台上查看打印结果的时候,一定要等定时器执行完改变状态之后再点开Promise对象,不然的话看到的结果还是定时器之前的结果,因为console.log()是个同步代码

当然抛出异常也一样会返回一个失败的失败的promise对象,所以这里也照样加一个try...catch(e)...

this.callBacks.push({
onResolved: function () {
	try {
		//执行成功的回调函数
		//成功的结果就是当前promise实例对象的PromiseResult
		//获取回调函数的执行结果
		let result = onResolved(_this.PromiseResult);
		//根据返回的结果判断这个promise的返回状态和值
		if (result instanceof MyPromise) {
			//如果是 MyPromise对象
			result.then(res => {
				resolve(res);
			}, err => {
				reject(err);
			})
		} else {
			//结果的对象状态为成功
			resolve(result);
		}
	} catch (e) {
		reject(e);
	}
},
onRejected: function () {
	//老规矩
	try {
		let result = onRejected(_this.PromiseResult);
		//根据返回的结果判断这个promise的返回状态和值
		if (result instanceof MyPromise) {
			//如果是 MyPromise对象
			result.then(res => {
				resolve(res);
			}, err => {
				reject(err);
			})
		} else {
			//结果的对象状态为成功
			resolve(result);
		}
	} catch (e) {
		reject(e);
	}
}
})

2.3.6、then的最后优化

到这里其实还有一个小问题。就是还没有对失败的状态做一个添加

可结果,promise的状态还是pending

造成这样的原因是,在then()中,并没有对失败的结果设置,所以就直接返回初始的promise

所以这里又是老规矩

try {
	let result = onRejected(this.PromiseResult);
	if (result instanceof MyPromise) {
		//如果是 MyPromise对象
		result.then(res => {
			resolve(res);
		}, err => {
			reject(err);
		})
	} else {
		//结果的对象状态为成功
		resolve(result);
	}
} catch (error) {
	reject(e);
}

然后就是优化,基本上每个判断都会有这样一串代码,所以这里把他给分装一下,单独做一块

	try {
		//执行成功的回调函数
		//成功的结果就是当前promise实例对象的PromiseResult
		//获取回调函数的执行结果
		let result = onResolved(_this.PromiseResult);
		//根据返回的结果判断这个promise的返回状态和值
		if (result instanceof MyPromise) {
			//如果是 MyPromise对象
			result.then(res => {
				resolve(res);
			}, err => {
				reject(err);
			})
		} else {
			//结果的对象状态为成功
			resolve(result);
		}
	} catch (e) {
		reject(e);
	}

then最终的结果:

then(onResolved, onRejected) {
    const _this = this;
    //调用回调函数  通过当前promise实例对象的PromiseState判断
    return new MyPromise((resolve, reject) => {
      //封装函数
      function callBack(fn) {
        try {
          //获取回调函数的执行结果
          let result = fn(_this.PromiseResult);//注意这里的this指向
          if (result instanceof MyPromise) {
            //如果是 MyPromise对象
            result.then(res => {
              resolve(res);
            }, err => {
              reject(err);
            })
          } else {
            //结果的对象状态为成功
            resolve(result);
          }
        } catch (e) {
          reject(e);
        }
      }
      if (this.PromiseState === FULFILLED) {//成功
        callBack(onResolved);
      } else if (this.PromiseState === REJECTED) {//失败
        callBack(onRejected);
      } else if (this.PromiseState === PENDING) {//待定
        //保存回调函数
        this.callBacks.push({
          onResolved: function () {
            callBack(onResolved);
          },
          onRejected: function () {
            callBack(onRejected);
          }
        })
      }
    })
  }

2.4、catch方法的异常穿透和值传递

2.4.1、原型上定义一个catch的方法

  1. catch是promise失败的回调,和then的第二个参数函数是一样的

  1. catch的形参也是一个函数

MyPromise.prototype = {
  constructor: MyPromise,
  /*添加 then() */
  then(onResolved, onRejected) {
    const _this = this;
    //调用回调函数  通过当前promise实例对象的PromiseState判断
    return new MyPromise((resolve, reject) => {
      //封装函数
      function callBack(fn) {
        try {
          //获取回调函数的执行结果
          let result = fn(_this.PromiseResult);
          if (result instanceof MyPromise) {
            //如果是 MyPromise对象
            result.then(res => {
              resolve(res);
            }, err => {
              reject(err);
            })
          } else {
            //结果的对象状态为成功
            resolve(result);
          }
        } catch (e) {
          reject(e);
        }
      }
      if (this.PromiseState === FULFILLED) {//成功
        callBack(onResolved);
      } else if (this.PromiseState === REJECTED) {//失败
        callBack(onRejected);
      } else if (this.PromiseState === PENDING) {//待定
        //保存回调函数
        this.callBacks.push({
          onResolved: function () {
            callBack(onResolved);
          },
          onRejected: function () {
            callBack(onRejected);
          }
        })
      }
    })
  },
  /* 添加catch方法 */
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
}

2.4.2、异常穿透

所谓异常穿透,如果说promise的状态修改为失败,那即使实例对象调用了多个then都不会执行,直接执行catch,并且在then中返回的promise对象是失败的结果,也会有异常穿透

 let p = new Promise((resolve, reject) => {
      reject("error");
    })

    p.then(res => {
      console.log(111);
    }).then(res => {
      console.log(222);
    }).then(res => {
      console.log(333);
    }).catch(err => {
      console.warn(err);
    })

    console.log(p);

先来看看原生的,这里可以看到then方法都没执行

再来看看自己封装的,可以看到已经报错了

其实这个原因很简单,就是如果此时promise的状态为失败,就肯定会去调then的第二个参数,然后再去reject方法中遍历执行,而此时,then中的第二参数没有定义,所以就报了函数未定义的错误。但是原生的promise允许不传第二个回调,所以我们也来实现一下

然后我们又回到这个then方法,在这里加一个判断就可以实现异常穿透了

2.4.3、值传递

值传递就是说,调用then()里面啥都不写,也能实现传递

let p = new Promise((resolve, reject) => {
	resolve("error");
})

p.then()
	.then(res => {
		console.log(222);
	}).then(res => {
		console.log(333);
	}).catch(err => {
		console.warn(err);
	})

console.log(p);

先看看原生的

再来看看自己的,又开始报错了,和之前一样也是说方法未定义

其实原因也是一样的,之前是第二个回调未定义,现在是两个回调都没定义,那就是两个undefined,显然不符合then内部的一个逻辑,都无法执行了。

又回到这个then方法,在这里加一个判断,和上面一样,反正就是不让函数参数为undefined

2.5、resolve方法封装

这个resolve不是之前的那个resolve,这个resolve是定义在构造函数本身的一个方法,只能通过构造函数本身自己调用的,实例对象不能调用

  1. resolve是构造函数本身的方法

  1. Promise.resolve()等价于new Promise(resolve => resolve())

  1. 无参返回的是一个结果为undefined并且状态为成功的promise对象

  1. 参数是非promise对象,那then返回结果是结果为这个形参的返回值并且状态为成功的promise对象

  1. 参数是promise对象,那这个promise对象的返回结果就是这个then的返回结果

  1. 参数抛出异常,那then返回的是一个结果为抛出异常的值并且状态为失败的promise对象

MyPromise.resolve = function (value) {
  return new MyPromise((resolve, reject) => {
    //老规矩
    try {
      if (value instanceof MyPromise) {
        value.then(res => {
          resolve(res);
        }, err => {
          reject(err);
        })
      } else {
        resolve(value);
      }
    } catch (error) {
     reject(error);
    }
  })
}

2.6、reject方法封装

reject和resolve一样也构造函数本身的方法,同样也是返回promise,但是不同的是,不管参数是什么,返回的都是失败的promise的对象

MyPromise.reject = function (value) {
  return new MyPromise((resolve, reject) => {
    reject(value);
  })
}

2.7、all方法封装

all()也是构造函数本身的方法,不同的是:

  1. 参数是有promise对象为元素组成的元素

  1. 只有参数的实例全部都是成功的,返回值也是由成功的promise的对象组成的数组

  1. 只要参数中有一个实例对象是失败的,那这个方法返回的就是失败的,失败的结果就是数组中失败的实例的结果

MyPromise.all = function (promises) {
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(res => {
				//不可以这样直接调
        resolve();
      }, err => {
        reject();
      })
    }
  })
}

不可以成功就调成功,如果数组里面的第一个实例成功就是调成功,那如果第二个失败了呢,在原生promise中,就会显示失败的结果,但是如果像上面这样调,那还会显示成功的结果,这显然不行

所以要判断每一个对象的状态都是否为成功,再去调用resolve()

MyPromise.all = function (promises) {
  return new MyPromise((resolve, reject) => {
    //声明计数变量
    let count = 0;
    //保存每个实例成功的结果
    let resolveBacks = [];
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(res => {
        //成功
        count++;
        //将当前promise对象成功的结果存入数组中
        resolveBacks[i] = res;
        //如果count的数量等于promises的数量,就说明全部成功了
        if (count === promises.length) {
          resolve(resolveBacks);
        }
      }, err => {
        reject(err);
      })
    }
  })
}

resolveBacks[i] = res 这行代码不推荐直接push,all里面的返回的数组是和参数一样的顺序,因为promise每个都是异步的,所以执行的时间不同,而push就是谁先调,谁就先进去数组,显然不符合all的特性

2.8、race方法封装

race()也是构造函数本身的方法,语法和all()类似,不同的是:他参数里的数组里的对象谁先改变状态,那这个方法就返回谁的状态和结果值

MyPromise.race = function (promises) {
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(res => {
        //只要进来,那这个方法的返回状态就是成功的
        resolve(res);
      }, err => {
        //只要进来,那这个方法的返回状态就是失败的
        reject(err);
      })
    }
  })
}

2.9、回调函数的异步执行

所谓回调函数的异步执行,话不多说,直接那原生promise看一下就知道了

let p = new Promise((resolve, reject) => {
	resolve("成功");
	console.log(111);
})

p.then(res => {
	console.log(222);
})

console.log(333);

一看就知道then()方法执行的最慢,那肯定就不是同步的,不是同步那就是异步,promise的then()是属于异步中的微任务

再来看一下自己封装的,很有顺序的111,222,333

所以我们来到then方法内部,让他变成异步的就行

在成功和失败这个地方加入queueMicrotask(),这个方法是让他内部的代码块为微任务执行,定时器的话也可以,但是,定时器是属于宏任务的,不符合这个方法的任务队列

还有一个地方要加 在构造函数里面的resolve和reject的方法中成功和失败的回调,也异步调用

3、完结

/* 定义promise的三个状态 */
const PENDING = 'pending';      //待定
const FULFILLED = 'fulfilled';  //成功
const REJECTED = 'rejected';    //失败

function MyPromise(executor) {
  //添加属性    起始值
  this.PromiseState = PENDING;
  this.PromiseResult = undefined;

  //保存回调函数
  this.callBacks = [];
  //保存实例对象的值
  const _this = this;

  //resolve
  const resolve = function (data) {
    //判断状态
    if (_this.PromiseState !== PENDING) return;

    //1.修改对象的成功状态 (PromiseState);
    _this.PromiseState = FULFILLED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;

    //调用成功的回调
    queueMicrotask(() => {
      _this.callBacks.forEach(item => {
        item.onResolved();
      })
    })

  }

  //reject
  const reject = function (data) {
    //判断状态
    if (_this.PromiseState !== PENDING) return;

    //1.修改对象的失败状态 (PromiseState);
    _this.PromiseState = REJECTED;

    //2.设置对象结果值 (PromiseResult);
    _this.PromiseResult = data;

    //调用失败的回调
    queueMicrotask(() => {
      _this.callBacks.forEach(item => {
        item.onRejected();
      })
    })
  }

  try {
    //同步调用 executor()
    executor(resolve, reject);
  } catch (e) {
    //抛出异常就调用reject修改状态为失败
    //抛出的数据 e 就promise失败的结果值
    reject(e)
  }
}

/* resolve方法 */
MyPromise.resolve = function (value) {
  return new MyPromise((resolve, reject) => {
    console.log(value);
    //老规矩
    try {
      if (value instanceof MyPromise) {
        value.then(res => {
          resolve(res);
        }, err => {
          reject(err);
        })
      } else {
        console.log(1);
        resolve(value);
      }
    } catch (error) {
      reject(error);
    }
  })
}

/* reject */
MyPromise.reject = function (value) {
  return new MyPromise((resolve, reject) => {
    reject(value);
  })
}

/* all */
MyPromise.all = function (promises) {
  return new MyPromise((resolve, reject) => {
    //声明计数变量
    let count = 0;
    //保存每个实例成功的结果
    let resolveBacks = [];
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(res => {
        //成功
        count++;
        //将当前promise对象成功的结果存入数组中
        resolveBacks[i] = res;
        //如果count的数量等于promises的数量,就说明全部成功了
        if (count === promises.length) {
          resolve(resolveBacks);
        }
      }, err => {
        reject(err);
      })
    }
  })
}

/* race */
MyPromise.race = function (promises) {
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(res => {
        //只要进来,那这个方法的返回状态就是成功的
        resolve(res);
      }, err => {
        //只要进来,那这个方法的返回状态就是失败的
        reject(err);
      })
    }
  })
}

/* MyPromise.prototype */
MyPromise.prototype = {
  constructor: MyPromise,
  /*添加 then() */
  then(onResolved, onRejected) {
    const _this = this;
    //判断回调函数参数
    if (typeof onRejected !== "function") {//相当于一个默认值
      onRejected = error => {
        throw error;
      }
    }
    if (typeof onResolved !== "function") {
      onResolved = value => value;
      //相当于
      // onResolved = function (value) {
      //  return value;
      // }
    }
    //调用回调函数  通过当前promise实例对象的PromiseState判断
    return new MyPromise((resolve, reject) => {
      //封装函数
      function callBack(fn) {
        try {
          //获取回调函数的执行结果
          let result = fn(_this.PromiseResult);
          if (result instanceof MyPromise) {
            //如果是 MyPromise对象
            result.then(res => {
              resolve(res);
            }, err => {
              reject(err);
            })
          } else {
            //结果的对象状态为成功
            resolve(result);
          }
        } catch (e) {
          reject(e);
        }
      }
      if (this.PromiseState === FULFILLED) {//成功
        queueMicrotask(() => {
          callBack(onResolved);
        })
      } else if (this.PromiseState === REJECTED) {//失败
        queueMicrotask(() => {
          callBack(onRejected);
        })
      } else if (this.PromiseState === PENDING) {//待定
        //保存回调函数
        this.callBacks.push({
          onResolved: function () {
            callBack(onResolved);
          },
          onRejected: function () {
            callBack(onRejected);
          }
        })
      }
    })
  },
  /* 添加catch方法 */
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
}

这个promise的封装终于结束了,其实大家回过头去看,也不会觉得会很难,主要就是then()这个地方有点绕,但是理清楚了也还是没问题的。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值