手撕promise
promise有三种状态
创建初始结构
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
this.status = Commitment.PENDING
this.result = null
func(this.resolve,this.reject)
}
resolve(result){
if(this.status === Commitment.PENDING){
this.status = Commitment.FULFILLED
this.result = result
}
}
reject(result){
if(this.status === Commitment.PENDING){
this.status = Commitment.REJECT
this.result = result
}
}
}
let commitment = new Commitment(resolve=>{
resolve('123')
})
写到这里直接new一个Commitment实例,结果会报错
为什么找不到status呢,因为constructor里面的this指向的是promise实例,而constructor外面指向的是其构造函数的原型对象
因此要修改this指向
修改this指向
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
this.status = Commitment.PENDING
this.result = null
func(this.resolve.bind(this),this.reject.bind(this))
}
接下来实现.then
class Commitment {
then(onFULFILLED,onREJECT){
if(this.status === Commitment.FULFILLED){
onFULFILLED(this.result)
}
if(this.status === Commitment.REJECT){
onREJECT(this.result)
}
}
}
解决异常
异常一
let promise = new Promise((resolve,reject)=>{
throw new Error('promise错误')
})
promise.then(
result=>{console.log(result)},
result=>{console.log(result.message)}
)
在原生promise中,如果promise自己报错,then的第二个函数能接收到,因此我们要用try…catch捕获
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
this.status = Commitment.PENDING
this.result = null
try{
func(this.resolve.bind(this),this.reject.bind(this))
} catch(error){
this.reject(error)
}
}
}
异常二
let promise = new Promise((resolve,reject)=>{
resolve('成功')
reject('失败')
})
promise.then(
undefined,
result=>{console.log(result.message)}
)
在原生promise中,.then里的两个参数如果不为函数,也不会报错;但是我们的手写代码会报错,因此要加异步判断
class Commitment {
then(onFULFILLED,onREJECT){
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
if(this.status === Commitment.FULFILLED){
onFULFILLED(this.result)
}
if(this.status === Commitment.REJECT){
onREJECT(this.result)
}
}
}
实现异步
要实现异步,先套上setTimeout
then(onFULFILLED,onREJECT){
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
if(this.status === Commitment.FULFILLED){
setTimeout(()=>{
onFULFILLED(this.result)
})
}
if(this.status === Commitment.REJECT){
setTimeout(()=>{
onREJECT(this.result)
})
}
}
之后会有一个问题
在说这个问题前,要先了解什么是宏任务和微任务
宏任务包括:
- 异步ajax请求
- setTimeout、setInterval
- 文件操作
- 其他
微任务包括:
- Promise.then .catch .finally(注意是Promise的这三个方法,Promise实例本身还是属于同步任务的)
- process.nextTick
- 其他
代码的执行流程为:同步任务–>微任务–>下一个宏任务–>下一个宏任务里的微任务
console.log('1')
let promise = new Promise((resolve,reject)=>{
console.log('2')
setTimeout(()=>{
resolve('5')
console.log('4')
})
})
promise.then(
result =>{console.log(result)}
)
console.log('3')
但是按照刚刚讲的,同步任务执行完输出123之后,应该先执行微任务.then,此时resolve(‘5’)还为执行,所以应该没有输出,之后再执行宏任务setTimeout输出4
结果应为1234
说明了,原生promise对于这里进行了处理
实现方法:
因为执行then之前没有执行resolve,导致then当时的状态为PENDING待定状态,所以我们加一步判断,判断当前状态为待定时,让then稍后再执行,执行完了resolve再执行then
具体的实现为,创建this.resolveCallbacks=[]保存resolve的执行结果
创建this.rejectCallbacks=[]保存reject的执行结果
在then里,判断当前状态为待定时,将对应的函数onFULFILLED,onREJECT都push到对应数组中
之后在resolve和reject里遍历数组
这里还有一个坑,resolve和reject在事件循环末尾执行,所以要在resolve和reject里加上setTimeout
实现代码
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
this.status = Commitment.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 === Commitment.PENDING){
this.status = Commitment.FULFILLED
this.result = result
this.resolveCallbacks.forEach(
callback=>{
callback(result)
}
)
}
})
}
reject(result){
setTimeout(()=>{
if(this.status === Commitment.PENDING){
this.status = Commitment.REJECT
this.result = result
this.rejectCallbacks.forEach(
callback=>{
callback(result)
}
)
}
})
}
then(onFULFILLED,onREJECT){
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
if(this.status === Commitment.PENDING ){
this.resolveCallbacks.push(onFULFILLED)
this.resolveCallbacks.push(onREJECT)
}
if(this.status === Commitment.FULFILLED){
setTimeout(()=>{
onFULFILLED(this.result)
})
}
if(this.status === Commitment.REJECT){
setTimeout(()=>{
onREJECT(this.result)
})
}
}
}
带大家理一下顺序
let promise = new Promise((resolve,reject)=>{
console.log('2')
setTimeout(()=>{
resolve('5')
console.log('4')
})
})
promise.then(
result =>{console.log(result)}
)
实现链式
把then中的功能返回一个新的promise就可以了
then(onFULFILLED,onREJECT){
return new Commitment((resolve,reject)=>{
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
if(this.status === Commitment.PENDING ){
this.resolveCallbacks.push(onFULFILLED)
this.resolveCallbacks.push(onREJECT)
}
if(this.status === Commitment.FULFILLED){
setTimeout(()=>{
onFULFILLED(this.result)
})
}
if(this.status === Commitment.REJECT){
setTimeout(()=>{
onREJECT(this.result)
})
}
})
}
最终代码
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
this.status = Commitment.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 === Commitment.PENDING){
this.status = Commitment.FULFILLED
this.result = result
this.resolveCallbacks.forEach(
callback=>{
callback(result)
}
)
}
})
}
reject(result){
setTimeout(()=>{
if(this.status === Commitment.PENDING){
this.status = Commitment.REJECT
this.result = result
this.rejectCallbacks.forEach(
callback=>{
callback(result)
}
)
}
})
}
then(onFULFILLED,onREJECT){
return new Commitment((resolve,reject)=>{
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
if(this.status === Commitment.PENDING ){
this.resolveCallbacks.push(onFULFILLED)
this.resolveCallbacks.push(onREJECT)
}
if(this.status === Commitment.FULFILLED){
setTimeout(()=>{
onFULFILLED(this.result)
})
}
if(this.status === Commitment.REJECT){
setTimeout(()=>{
onREJECT(this.result)
})
}
})
}
}
|