手撕promise--promise的底层实现<究极详细>

手撕promise

promise有三种状态

请添加图片描述

创建初始结构
// 初始结构
class Commitment {
  static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
  constructor(func){
    // status状态
    this.status = Commitment.PENDING
    // result对应状态的结果
    this.result = null
    func(this.resolve,this.reject)
  }
  // 因为resolve和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){
    // status状态
    this.status = Commitment.PENDING
    // result对应状态的结果
    this.result = null
      // 在这里为resolve和reject函数修改this指向
    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)
    }
  }
}
解决异常
异常一
// 原生promise
let promise = new Promise((resolve,reject)=>{
    throw new Error('promise错误')
})
promise.then(
  result=>{console.log(result)},
  result=>{console.log(result.message)}// promise错误
)

在原生promise中,如果promise自己报错,then的第二个函数能接收到,因此我们要用try…catch捕获

class Commitment {
  static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
  constructor(func){
    // status状态
    this.status = Commitment.PENDING
    // result对应状态的结果
    this.result = null
    try{
      func(this.resolve.bind(this),this.reject.bind(this))
    } catch(error){
      this.reject(error)
    }
  }
}
异常二
// 原生promise
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)
      })
    }
  }

之后会有一个问题

在说这个问题前,要先了解什么是宏任务和微任务

宏任务包括:

  • script整体代码

  • 异步ajax请求

  • setTimeout、setInterval

  • 文件操作

  • 其他

微任务包括:

  • Promise.then .catch .finally(注意是Promise的这三个方法,Promise实例本身还是属于同步任务的)
  • process.nextTick
  • 其他

因为script本身就是一个宏任务,所以事件循环本质上是从宏任务开始的,先执行宏任务里的微任务,再执行宏任务,再执行另一个宏任务,依次反复。当然同步任务是最先执行。

// 原生promise
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')

// 输出结果为12345

但是按照刚刚讲的,同步任务执行完输出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){
    // status状态
    this.status = Commitment.PENDING
    // result对应状态的结果
    this.result = null
    // 保存resolve函数
    this.resolveCallbacks=[]
    // 保存reject函数
    this.rejectCallbacks=[]
    try{
      func(this.resolve.bind(this),this.reject.bind(this))
    } catch(error){
      this.reject(error)
    }
  }
  // 因为resolve和reject也是方法
  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 : ()=>{}
    // 这一步判断PENDING状态是为了实现异步
    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)}
)

/*
**new一个实例输出2,运行遇到setTimeout异步先不执行,到then方法
**then里面发现现在的状态是待定,就把函数参数push到对应数组
**现在开始执行setTimeout里面的内容,先执行resolve
**发现resolve函数上来就是一个setTimeout异步,只能出来先执行console.log('4'),输出4
**最后执行在消息队列中的resolve函数
*/
实现链式

把then中的功能返回一个新的promise就可以了

  then(onFULFILLED,onREJECT){
    return new Commitment((resolve,reject)=>{
      // 如果参数是一个函数,就把自己赋值给自己(不变),如果不是函数,则赋一个空函数
      onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
      onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
      // 这一步判断PENDING状态是为了实现异步
      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){
    // status状态
    this.status = Commitment.PENDING
    // result对应状态的结果
    this.result = null
    // 保存resolve函数
    this.resolveCallbacks=[]
    // 保存reject函数
    this.rejectCallbacks=[]
    try{
      func(this.resolve.bind(this),this.reject.bind(this))
    } catch(error){
      this.reject(error)
    }
  }
  // 因为resolve和reject也是方法
  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 : ()=>{}
      // 这一步判断PENDING状态是为了实现异步
      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)
        })
      }
    })
  }
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的战斗鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值