- 初始结构
- 坑点:this指向问题
- then
- 执行异常
- then的参数
- 异步
- 回调保存
- 坑点:resolve和reject在事件循环末尾执行
- 链式调用
- 完整代码
初始结构
-
原生的promise使用new创建一个实例,传入的参数是一个函数,会自动执行。原生的Promise可以传入resolve和reject两个参数。调用resolve是以函数形式调用的:resolve(),可以传入参数resolve('okok')
-
promise有三种状态:Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)。一个 Promise 对象只能从 Pending 状态转换到 Fulfilled 或 Rejected 状态,并且状态转换是不可逆的。
let promise = new Promise((resolve,reject) =>{resolve('okok');
});
class MyPromise{static PENDING = '待定'; static FULFILLED='成功'; static REJECTED = '失败';constructor(func){this.status = MyPromise.PENDING;this.result = null;//每个实例都有result属性func(this.resolve, this.reject);}resolve(result){if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = result;//把参数赋值给实例的result属性}}reject(result){if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = result}}
}
坑点:this指向问题
-
上面代码执行结果:
-
分析原因:new一个新实例的时候执行的是constructor里的内容,在新实例被创建后再在外部环境下执行resolve,相当于不在class内部使用这个this,外部没有所有会报undefined。
-
解决办法:使用bind绑定:固定 this指向,确保 resolve和 reject的 this始终是 MyPromise实例
class MyPromise{static PENDING = '待定'; static FULFILLED='成功'; static REJECTED = '失败';constructor(func){this.status = MyPromise.PENDING;this.result = null;func(this.resolve.bind(this), this.reject.bind(this));}resolve(result){if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = result}}reject(result){if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = result}}
}
then
- then方法可以传入两个参数。两个参数都是函数。一个是当状态为成功时执行的代码,一个是当状态为失败时执行的代码
let promise = new Promise((resolve,reject) =>{resolve('okok');reject('nono');
});
promise.then(result => {console.log(result)},(error) => {console.log(error.message);}
);
- 执行结果只有一个,所以手写时进行判断。如果当前实例的状态为成功的话,执行传进来的onFULFILLED函数,并传入前面保留的result属性值;错误类似
class MyPromise {static PENDING = "待定";static FULFILLED = "成功";static REJECTED = "失败";constructor(func) {this.status = MyPromise.PENDING;this.result = null;func(this.resolve.bind(this), this.reject.bind(this));}resolve(result) {if (this.status === MyPromise.PENDING) {this.status = this.FULFILLED;this.result = result;}}reject(result) {if (this.status === MyPromise.PENDING) {this.status = this.REJECTED;this.result = result;}}then(onFULFILLED, onREJECTED) {if (this.status === MyPromise.FULFILLED) {onFULFILLED(this.result);}if (this.status === MyPromise.REJECTED) {onREJECTED(this.result);}}
}
执行异常
- 执行函数里面抛出错误是会触发拒绝方法的。调用then可以把错误信息作为内容输出出来
- 手写实现同样功能。在执行resolve和reject前进行判断,如果有报错就把错误信息传给reject方法,并直接执行reject方法。这里不用this绑定:这里是直接执行,不是创建实例后再执行。
constructor(func) {this.status = MyPromise.PENDING;this.result = null;try{func(this.resolve.bind(this), this.reject.bind(this))}catch(error){this.reject(error)}}
then的参数
- 原生promise里规定then的两个参数如果不是函数的话就要忽略
- then函数中使用条件运算符:把不是函数的参数改为函数
then(onFULFILLED, onREJECTED) {onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : ()=>{}if (this.status === MyPromise.FULFILLED) {onFULFILLED(this.result);}if (this.status === MyPromise.REJECTED) {onREJECTED(this.result);}}
异步
-
原生的异步执行顺序:
-
给手写的代码在then方法里面添加setTimeout就可以了,需要在if判断状态符合再添加异步
then(onFULFILLED, onREJECTED) {onFULFILLED = typeof onFULFILLED === "function" ? onFULFILLED : () => {};onREJECTED = typeof onREJECTED === "function" ? onREJECTED : () => {};if (this.status === MyPromise.FULFILLED) {setTimeout(() => {onFULFILLED(this.result);});}if (this.status === MyPromise.REJECTED) {setTimeout(() => {onREJECTED(this.result);});}}
回调保存
-
原生
-
手写
-
手写的结果对比原生差了resolve的执行的okok。原因:then方法里面是根据条件判断来执行的代码,没有符合的状态就不会执行。then里面没有定义待定状态应该做什么。
-
解决:then方法中添加待定的情况。这个时候resolve和reject还没获取到任何值。必须让then稍后执行,等resolve执行了再执行。1、为了保留then里的函数,创建数组来保存函数,在实例化的时候就让每个实例都有这两个数组。一个保存resolve函数,一个保存reject函数。2、当状态是待定时,把then里的两个参数放在两个数组里。3、执行resolve/reject时遍历自身的callbacks数组,有then保存过来的待执行的函数逐个执行。
class MyPromise{static PENDING = '待定'; static FULFILLED='成功'; static REJECTED = '失败';constructor(func){this.status = MyPromise.PENDING;this.result = null;this.resolveCallbacks = [];//第一步this.rejectCallbacks = [];//第一步try{func(this.resolve.bind(this), this.reject.bind(this))}catch(error){this.reject(error)}}resolve(result){if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = resultthis.resolveCallbacks.forEach(callback => {callback(result)})//第三步}}reject(result){if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = resultthis.rejectCallbacks.forEach(callback => {callback(result)})//第三步}}then(onFULFILLED, onREJECTED){onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : ()=>{}if(this.status === MyPromise.PENDING){//第二步this.resolveCallbacks.push(onFULFILLED);this.rejectCallbacks.push(onREJECTED);}if(this.status === MyPromise.FULFILLED){setTimeout(()=>{onFULFILLED(this.result)})}if(this.status === MyPromise.REJECTED){setTimeout(()=>{onREJECTED(this.result)})}}
}
坑点:resolve和reject在事件循环末尾执行
上面代码的执行结果是:
- resolve是异步,应该先输出“第四步”,解决:只需要给resolve和reject里面加上setTimeout即可
resolve(result){setTimeout(()=>{if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = resultthis.resolveCallbacks.forEach(callback => {callback(result)})}});}reject(result){setTimeout(()=>{if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = resultthis.rejectCallbacks.forEach(callback => {callback(result)})}})}
链式调用
- then中返回一个新的手写Promise实例
then(onFULFILLED, onREJECTED){return new MyPromise((resolve,reject)=>{onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : ()=>{}if(this.status === MyPromise.PENDING){this.resolveCallbacks.push(onFULFILLED);this.rejectCallbacks.push(onREJECTED);}if(this.status === MyPromise.FULFILLED){setTimeout(()=>{onFULFILLED(this.result)})}if(this.status === MyPromise.REJECTED){setTimeout(()=>{onREJECTED(this.result)})}}) }
完整代码
class MyPromise{static PENDING = '待定'; static FULFILLED='成功'; static REJECTED = '失败';constructor(func){this.status = MyPromise.PENDING;this.result = null;this.resolveCallbacks = [];this.rejectCallbacks = [];try{func(this.resolve.bind(this), this.reject.bind(this))}catch(error){this.reject(error)}}resolve(result){setTimeout(()=>{if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = resultthis.resolveCallbacks.forEach(callback => {callback(result)})}});}reject(result){setTimeout(()=>{if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = resultthis.rejectCallbacks.forEach(callback => {callback(result)})}})}then(onFULFILLED, onREJECTED){return new MyPromise((resolve,reject)=>{onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : ()=>{}if(this.status === MyPromise.PENDING){this.resolveCallbacks.push(onFULFILLED);this.rejectCallbacks.push(onREJECTED);}if(this.status === MyPromise.FULFILLED){setTimeout(()=>{onFULFILLED(this.result)})}if(this.status === MyPromise.REJECTED){setTimeout(()=>{onREJECTED(this.result)})}}) }
}