1.了解Promise
众所周知,Promise是JS中异步编程的一种解决方案。
在模拟实现Promise之前,首先需要了解Promise的特性。
1.一个promise(Promise的实例),有三种状态,初始时的等待态pending,成功时的成功态resolved以及失败态rejected。promise的状态只能被改变一次,只能从pending成为resolved或者rejected,改变不可逆。
2.promise改变状态的方式有三种,当调用resolve时,由pending变为resolved,当调用reject,或者执行器函数内部抛出异常时,由pending变为rejected。
3.对同一个promise指定了多个回调函数时,当promise变为对应状态时,对应的回调函数全部都会执行。
4.p.then() 返回的Promise的结果状态怎么确定?
由then指定的回调函数的结果决定
①如果抛出异常,新的promise变成rejected
②如果回调函数返回的是不是Promise的任意值string,undefined等等,新的promise变为fulfilled,数据是返回的值
③如果回调函数返回的是一个promise对象。返回值promise对象的结果和状态就是新的promise的结果和状态
5.promise异常穿透:在之前的then中只写了一个成功的回调,没有写失败的回调 相当于隐式的写了 err=>{throw err} 所以会穿透
①当使用promise的then链式调用时,可以在最后指定失败的回调
②前面的任何操作出现了异常都会传到最后的失败的回调函数中处理
6.中断promise链:当使用promise链式调用时,在中间中断,不再调用后面的回调函数
方法:在需要中断处 返回一个pending状态的promise return new Promise(()={})
2.Promise的模拟实现
实现Promise中的then、catch、all、race、resolve、reject方法。
使用的时ES6语法。
1.MyPromise整体结构
class MyPromise { //构造函数 参数是执行器函数 constructor(executor) { } //指定成功和失败的回调函数,返回一个新的promise then(resolvedCallBack, rejectedCallBack = error => { throw error }) { } //返回一个新的promise catch(rejectedCallBack) { } /** * @author: forceddd * @desc: 要判断传入的数组是否全部是promise对象 * @param ArrayOfPromises * @return: 返回最先完成的promise任务结果和状态 无论成功或者失败 */ static race(promises) { } /** * @author: forceddd * @desc: 当所有promise都成功时,返回一个成功态的promise 数据是所有promise成功数据组成的数组 否则返回一个失败态的promise * @param ArrayOfPromises * @return: promise */ static all(promises) { } /** * @author: forceddd * @desc: 传入非promise 返回一个成功态的promise data是该参数 * 传入promise 返回值的状态和结果由参数promise的状态和结果决定 * @param any * @return: 返回一个指定结果的promise */ static resolve(data) { } /** * @author: forceddd * @desc: 不考虑传入promise * @param reason * @return: 返回一个失败状态的promise 失败原因是传入的参数 */ static reject(error) { }}
2.构造器的实现
//构造函数 参数是执行器函数(执行器函数是同步执行的) constructor(executor) { this.status = 'pending';//当前实例的状态 this.data = undefined;//用于存储完成后的数据 this.callBacks = [];//用于存储promise完成之前,即状态仍为pending时,便指定的回调函数 [{resolveCb,rejectCb}] //箭头函数让this指向构造函数中的this 即实例 否则resolve和reject中的this会指向window //声明两个用于改变promise状态的函数,并且要作为参数传入执行器函数executor中 const resolve = data => { //status只能被修改一次 判断当前是否是初始值pending if (this.status !== 'pending') return; //改变promise状态为成功态 this.status = 'resolved'; //存储成功值 this.data = data; //如果此时有的回调函数已经被指定了 应当立即--异步地--执行这些回调函数 //通过setTimeout将成功地回调函数放入异步队列 if (this.callBacks.length) { setTimeout(() => { this.callBacks.forEach(cbObj => cbObj.resolvedCallBack(data)) }) } } const reject = error => { //status只能被修改一次 判断当前是否是初始值pending if (this.status !== 'pending') return; //改变promise状态为失败态 this.status = 'rejected'; //存储失败信息 this.data = error; //如果此时有的回调函数已经被指定了 应当立即--异步--执行这些回调函数 //通过setTimeout将失败地回调函数放入异步队列 if (this.callBacks.length) { setTimeout(() => { this.callBacks.forEach(cbObj => cbObj.rejectedCallBack(error)) }) } } // 同步执行executor try catch用于捕获executor抛出的异常 try { executor(resolve, reject) } catch (error) { //如果抛出了异常,promise要变为rejected reject(error) } }
3.then的实现
//指定成功和失败的回调函数,返回一个新的promise //给rejectedCallBack默认值 实现异常穿透处理 即不传入rejectedCallBack, //但又是失败态时,会执行默认函数,抛出异常,将then返回值promise转为失败态 then(resolvedCallBack, rejectedCallBack = error => { throw error }) { //因为在catch中 resolvedCallBack我会传入一个null 所以此处增加一个判断条件 resolvedCallBack = typeof resolvedCallBack === 'function' ? resolvedCallBack : _ => _; //then的返回值是一个promise return new MyPromise((resolve, reject) => { /** * @desc: 处理then的返回值 * 1.如果执行回调函数时抛出了异常 返回一个失败的promise error是异常信息 2.如果回调函数返回值不是promise对象 ,返回一个成功的promise data是回调函数返回值 3.如果回调函数的返回值是一个promise 这个promise的状态和结果作为返回值promise的状态和结果 * @param: cb * @return: */ const handleReturn = cb => { let res;//接收成功或者失败的回调函数的返回值 try { res = cb(this.data); res instanceof MyPromise //3.如果回调函数的返回值是一个promise 这个promise的状态和结果作为返回值promise的状态和结果 ? res.then( data => resolve(data), error => reject(error) ) //简写形式 成功时 将resolve作为回调函数resolvedCallBack传入 内部会执行 resolvedCallBack(this.data), //和传入一个data => resolve(data)效果相同 // res.then(resolve, reject) //2.如果回调函数返回值不是promise对象 ,返回一个成功的promise data是回调函数返回值 : resolve(res) } catch (error) { //1.如果执行回调函数时抛出了异常 返回一个失败的promise error是异常信息 reject(error) } } if (this.status === 'pending') { //此时实例仍然时pending态 回调函数已经被声明了 将指定的回调函数存储到callBacks中 //此时同样要根据回调函数返回值来判断then的返回值 所以不能直接像下面一样存储回调函数,需要外包一层函数,以便对返回值进行判断 // this.callBacks.push({ resolvedCallBack, rejectedCallBack }) this.callBacks.push({ resolvedCallBack: _ => handleReturn(resolvedCallBack), rejectedCallBack: _ => handleReturn(rejectedCallBack) }) /* 在状态改变之后 指定的回调函数 会立即--异步--执行 */ } else if (this.status === 'resolved') { setTimeout(() => handleReturn(resolvedCallBack)); } else { setTimeout(() => handleReturn(rejectedCallBack)); } }) }
4.catch的实现
//返回一个新的promise catch是then的一个语法糖 catch(rejectedCallBack) { return this.then(null, rejectedCallBack) }
5.resolve和reject的实现
/** * @author: forceddd * @desc: 传入非promise 返回一个成功态的promise data是该参数 * 传入promise 返回值的状态和结果由参数promise的状态和结果决定 * @param any * @return: 返回一个指定结果的promise */static resolve(data) { return new MyPromise((resolve, reject) => { data instanceof MyPromise ? /*res.then( data => resolve(data), error => reject(error) ) 的简写 */ data.then(resolve, reject) : resolve(data) }) } /** * @author: forceddd * @desc: 不考虑传入promise * @param reason * @return: 返回一个失败状态的promise 失败原因是传入的参数 */ static reject(error) { return new MyPromise((_, reject) => reject(error)) }
6.race的实现
/** * @author: forceddd * @desc: 要判断传入的数组是否全部是promise对象 * @param ArrayOfPromises * @return: 返回最先完成的promise任务结果 无论成功或者失败 */ static race(promises) { //返回一个promise return new MyPromise((resolve, reject) => { promises.forEach(promise => { //判断元素是不是promise 如果不是,通过resolve函数转换成promise promise = promise instanceof MyPromise ? promise : MyPromise.resolve(promise); //将最先完成的promise状态和结果 作为race返回值的状态和结果 promise.then(resolve, reject) }) }) }
7.all的实现
/** * @author: forceddd * @desc: 当所有promise都成功时,返回一个成功态的promise 数据是所有promise成功数据组成的数组 否则返回一个失败态的promise * @param ArrayOfPromises * @return: promise */ static all(promises) { let resolveCount = 0;//记录执行then中成功回调函数的次数 每执行一次then的回调函数说明有个一个promise已经有了结果 const results = new Array(promises.length)//创建一个长度和promises相同的数组,用来存储成功的data return new MyPromise((resolve, reject) => { promises.forEach((promise, index) => { //将不是promise的元素转换成promise promise = promise instanceof MyPromise ? promise : MyPromise.resolve(promise); promise.then( data => { results[index] = data; resolveCount++ resolveCount === promises.length ? resolve(results) : null }, error => reject(error) //将all的返回值变为reject状态 ) }) }) }