手写Promise

我们在去学习Promise的时候最先需要知道Promise的三种状态

Promise有三种状态分别是pending fulfilled 还有rejected状态所以在我们去手写Promise的时候可以创建一个对象负责去保管 pending fulfilled rejected

//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
    PENDING:0,  // pending 
    FULFILLED:1, // fulfilled
    REJECTED:2  // rejected
}

首先我们用class构造函数创建一个MyPromise的类,并且将初始状态设置为pending状态

//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
    PENDING:0,  // pending 
    FULFILLED:1, // fulfilled
    REJECTED:2  // rejected
}

class MyPromise{
    //创建一个变量来存Promise状态
    #state = PROMISE_STATE.PENGDING     //#是为了保持变量的私有性,不会被修改   
    //接收一个执行器作为参数
    constructor(executor) {
    executor(this.#resolve,this.#reject)  
    }
    //设置私有的resolve()用来存储成功的对象
    #resolve(value){
        console.log('resolve调用了',value)    
    }
    //设置私有的reject()用来存储失败的对象
    #reject() { }
}
const mp = new MyPromise((resolve,reject)=>{
    resolve('成功的')

})

我们可以从上面写的代码可以看见已经给我的MyPromise构造函数的constructor传了一个回调函数executor也就是我们常说的执行器函数。并且我们为了保证我们的MyPromise对象有一定的安全性,不让轻易修改,这里我们用到了#去对属性和方法都进行了加密。也就是用到了我们面向对象的封装性。

我们在调用resolve的时候可以发现已经传入过去了resolve的值,但是我们Promise对象是要保存数据,我们现在还并没有把数据存起来的能力,所以请看下面代码.

//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
    PENDING:0,  // pending 
    FULFILLED:1, // fulfilled
    REJECTED:2  // rejected
}

class MyPromise{
    //创建一个变量用来存Promise的结果
    #result 
    //创建一个变量来存Promise状态
    #state = PROMISE_STATE.PENGDING     //#是为了保持变量的私有性,不会被修改      
    //接收一个执行器作为参数
    constructor(executor) {
    // executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
    executor(this.#resolve.bind(this),this.#reject.bind(this)) //调用回调函数并且将this指向实例   
    }
    //设置私有的resolve()用来存储成功的对象
    #resolve(value){
        console.log('resolve调用了',value)
        this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
    }
    //设置私有的reject()用来存储失败的对象
    #reject() { }
}
const mp = new MyPromise((resolve,reject)=>{
    resolve('成功的')

})

上述方法我们给#result赋值会出现错误是因为我们的#resolve在外面是以函数形式调用的所以我们用了bind()方法去改变了#resolve的this指向。当然我们在这直接把#resolve改成箭头函数也可以但是我们这个时候就要注意到,我们在类里面用箭头函数的话,这个方法是在实例对象里面,不再是在原型里面的方法。所以我们在手写Promise的时候去调用resolve或者reject要注意我们的this指向要保持在实例上。但是其实我们又遇到了一个问题,我就只单独说一下。在调用的时候我们Promise对象改变状态是不能够再改变的,也就是Promise只能修改一次。所以我们要为了严谨性要加上一个判断,如果我们已经改变状态了,我们就直接return退出函数

//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
    PENDING:0,  // pending 
    FULFILLED:1, // fulfilled
    REJECTED:2  // rejected
}

class MyPromise{
    //创建一个变量用来存Promise的结果
    #result 
    //创建一个变量来存Promise状态
    #state = PROMISE_STATE.PENGDING     //#是为了保持变量的私有性,不会被修改      
    //接收一个执行器作为参数
    constructor(executor) {
    // executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
    executor(this.#resolve.bind(this),this.#reject.bind(this)) //调用回调函数并且将this指向实例   
    }
    //设置私有的resolve()用来存储成功的对象
    #resolve(value){
          if(this.#state !== PROMISE_STATE.PENGDING  ) return
          console.log('resolve调用了',value)
          this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
          this.#state =  PROMISE_STATE.FULFILLED //使用了一次之后就会改变Promise的状态      
    }
    //设置私有的reject()用来存储失败的对象
    #reject() { }
}
const mp = new MyPromise((resolve,reject)=>{
    resolve('成功的')
})

上面已经基本完成了存储数据的方法了,现在我们就要开始写用来读取数据的then()方法,有两个参数,一个读取成功的(onFulfilled)一个读取失败的(onRejected)

在这里我们就可以更加清晰的知道这then方法里面的参数和resolve不是同一个东西

//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
    PENDING:0,  // pending 
    FULFILLED:1, // fulfilled
    REJECTED:2  // rejected
}

class MyPromise{
    //创建一个变量用来存Promise的结果
    #result 

    //创建一个变量来存Promise状态
    #state = PROMISE_STATE.PENGDING     //#是为了保持变量的私有性,不会被修改  
    
    //接收一个执行器作为参数
    constructor(executor) {

    // executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
    executor(this.#resolve.bind(this),this.#reject.bind(this)) //调用回调函数并且将this指向实例 
    }
    //设置私有的resolve()用来存储成功的对象
    #resolve(value){
          if(this.#state !==PROMISE_STATE.PENGDING) return
          console.log('resolve调用了',value)
          this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
          this.#state = PROMISE_STATE.FULFILLED //使用了一次之后就会改变Promise的状态
      
    }
    //设置私有的reject()用来存储失败的对象
    #reject(reason) { 
        if(this.#state !==PROMISE_STATE.PENGDING) return
          this.#result = reason
          this.#state = PROMISE_STATE.REJECTED//使用了一次之后就会改变Promise的状态
    }
    

    //读取数据的then方法
    then(onFulfilled,onRejected){
    if(this.#state === PROMISE_STATE.FULFILLED){
        onFulfilled(this.#result)
            }

        }


    }

const mp = new MyPromise((resolve,reject)=>{
    resolve('成功的')

    })
 mp.then(result=>{
        console.log('读取数据',result) 
     })

这个样子其实手写promise的雏形已经出来了,但是其实这里还是存在一点小问题,不知道你们能不能看得出来。其实就是如果我们在用异步去执行resolve或者reject的时候是有问题的。那么我们应该怎么去对以上代码进行一系列的操作去完善呢。请听我慢慢分析:首先我们去调用then方法的时候如果我们用异步的方法去执行resolve或者reject我们是还拿不到数据的,为什么呢?因为我们在这里并没有对then方法去做异步的处理,我们只能读取我们存进Promise的数据,不能拿到异步存储的数据,所以接下来我们就是要去解决这个问题了。其实我们主要就是要去判断我们的resolve是不是被调用了,如果resolve被调用我们去调用then就没有问题了

//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
    PENDING:0,  // pending 
    FULFILLED:1, // fulfilled
    REJECTED:2  // rejected
}

class MyPromise{
    //创建一个变量用来存Promise的结果
    #result 

    //创建一个变量来存Promise状态
    #state = PROMISE_STATE.PENGDING     //#是为了保持变量的私有性,不会被修改  
     
    //创建一个变量存储回调函数 也是一个私有属性
    #callback
    //接收一个执行器作为参数
    constructor(executor) {

    // executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
    executor(this.#resolve.bind(this),this.#reject.bind(this)) //调用回调函数并且将this指向实例 
    }
    //设置私有的resolve()用来存储成功的对象
    #resolve(value){
          if(this.#state !==PROMISE_STATE.PENGDING) return
          console.log('resolve调用了',value)
          this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
          this.#state = PROMISE_STATE.FULFILLED //使用了一次之后就会改变Promise的状态
            
          //当resolve执行时,说明数据已经来了,需要then的回调函数
          this.#callback && this.#callback(this.#result)
      
    }
    //设置私有的reject()用来存储失败的对象
    #reject(reason) { 
        if(this.#state !==PROMISE_STATE.PENGDING) return
          this.#result = reason
          this.#state = PROMISE_STATE.REJECTED//使用了一次之后就会改变Promise的状态
    }
    

    //读取数据的then方法
    then(onFulfilled,onRejected){
    //在这里我们还要先做一次判断是pending状态就说明我们还没有执行resolve
    if(this.#state === PROMISE_STATE.PENDING){
        this.#callback = onFulfilled  //将回调函数设置为callback的值
 
    }else if(this.#state === PROMISE_STATE.FULFILLED){
        onFulfilled(this.#result)
            }

        }


    }

const mp = new MyPromise((resolve,reject)=>{
    resolve('成功的')

    })
 mp.then(result=>{
        console.log('读取数据',result) 
     })

这个样子我们就可以把异步的也去完成了。但是,但是,但是,重要的事情说三遍,我们Promise的方法是在微任务队列里面的,很明显我们这里就是在调用回调函数而已,并没有达到Promise的then方法在微任务队列遵循事件循环。所以我们需要添加一个函数,queueMicrotask()这个函数就可以做到让then方法放到微任务队列里面了,当然我们的Promise还差一步链式调用所以在最后我们应该把then的方法返回一个新的Promise对象也给加进去

//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
  PENDING: 0,  // pending 
  FULFILLED: 1, // fulfilled
  REJECTED: 2  // rejected
}

class MyPromise {
  //创建一个变量用来存Promise的结果
  #result

  //创建一个变量来存Promise状态
  #state = PROMISE_STATE.PENGDING     //#是为了保持变量的私有性,不会被修改  

  //创建一个变量存储回调函数 也是一个私有属性
  //
  #callbacks = []
  //接收一个执行器作为参数
  constructor(executor) {

    // executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
    executor(this.#resolve.bind(this), this.#reject.bind(this)) //调用回调函数并且将this指向实例 
  }
  //设置私有的resolve()用来存储成功的对象
  #resolve(value) {
    if (this.#state !== PROMISE_STATE.PENGDING) return
    console.log('resolve调用了', value)
    this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
    this.#state = PROMISE_STATE.FULFILLED //使用了一次之后就会改变Promise的状态

    //当resolve执行时,说明数据已经来了,需要then的回调函数

    queueMicrotask(() => {
      this.#callbacks.forEach(cb => {
            cb()
       })
    })

  }
  //设置私有的reject()用来存储失败的对象
  #reject(reason) {
    if (this.#state !== PROMISE_STATE.PENGDING) return
    this.#result = reason
    this.#state = PROMISE_STATE.REJECTED//使用了一次之后就会改变Promise的状态
  }


  //读取数据的then方法
  then(onFulfilled, onRejected) {
       //then中回调函数的返回值,会成为新的Promise中的数据
    return new MyPromise(() => {
        //在这里我们还要先做一次判断是pending状态就说明我们还没有执行resolve
    if (this.#state === PROMISE_STATE.PENDING) {
      this.#callback = onFulfilled  //将回调函数设置为callback的值
       this.#callbacks.push(() => {
             resolve(onFulfilled(this.#result))             
          })
      })
    } else if (this.#state === PROMISE_STATE.FULFILLE) {
      //then的回调函数,放在微任务队列里面
      queueMicrotask(() => {
         resolve(onFulfilled(this.#result))
      })

    }

  }

 })
    

}

const mp = new MyPromise((resolve, reject) => {
  resolve('成功的')

})
mp.then(result => {
  console.log('读取数据', result)
})

这里我们自己的Promise对象就搞定了,当然我写的还有很多不足,在后面的学习我会进一步优化然后用es5的方式再写一次

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值