手动实现简易Promise

84 阅读3分钟

初始化结构

此时先新建一个class类,因为原生promise需要new一个实例,所以我们需要类和构造函数 PS:我常常忘记解释,但我会尽量注释,写此篇时默认你了解同步异步js运行机制和promise机制 (关于js同步异步可以见这篇) 微任务/宏任务和同步/异步之间的关系

class Pro {
  // promise的三种状态
  static PENDING = "待定";
  static fulfilled = "成功";
  static rejected = "拒绝";
  constructor(fn) {
      // 构造函数接收一个函数并执行(同步)
    // 初始化一个状态
    this.status = pro.PENDING
    // 结果
    this.result = null
    fn(this.resolve., this.reject)
  }
  resolve(res) {
  // 如果状态为待定则赋值成功
    if (this.status === pro.PENDING) {
      this.status = pro.fulfilled
       // 赋值结果
      this.result = res
    }
  }
    // 和resolve类似暂时放空
  reject() {

  }
    // .then回调
  then(onFulfilled, onRejected) {
    // 两个函数参数分别对应resolve,reject,且同时只能有一个
    if (this.status === pro.fulfilled) {
      // resolve 执行并返回结果
      onFulfilled(this.result)
    }
    // 和resovle对应,暂时放空
    if (this.status === pro.rejected) {
      // reject 执行并返回结果
      onRejected(this.result)
    }
  }
}

实例化测试一下先

操作和原生promise类似

console.log(1);
let pro = new Pro((resolve, reject) => {
  resolve('myPro')
  console.log(2);
})
pro.then(
  (res) => {
    console.log(res);
  },
  (rejRes) => {
    console.log(rejRes);
  },
)
console.log(3);

果然报错,看看是啥问题

我用心学习,就像我用心喜欢雨晴

原来是resolve里的this指向问题,没有指到实例上,打印出来是undefined,所以这里我用bing(this)给他指路指到实例上

  fn(this.resolve.bind(this), this.reject.bind(this))

再打印,这时可以了,但执行顺序不对,没有进行异步,正确的应该是1 2 3 mypro,因为then是异步

在这里插入图片描述

在执行then时加个定时器完成异步

    if (this.status === Pro.fulfilled) {
      // resolve 执行并返回结果
      // 定时器进行异步
      setTimeout(() => {
        onFulfilled(this.result)
      })
    }

在这里插入图片描述

这时再完善,在实例化函数里加个定时器

console.log(1);
let pro = new Pro((resolve, reject) => {
  console.log(2);
    // 加个定时器查看当resolve函数也成异步时,then会怎么执行
  setTimeout(() => {
    resolve('myPro')
    console.log(4);
  })
})
pro.then(
  (res) => {
    console.log(res);
  },
  (rejRes) => {
    console.log(rejRes);
  },
)
console.log(3);

执行

在这里插入图片描述

发现没有输出 myPro 推测是then没有执行,而then里是通过if判断状态是否执行函数参数onFulfilled,于是在then和定时器里分别打印状态

  // .then回调
  then(onFulfilled, onRejected) {
    console.log(1,this.status);
    // 两个函数参数分别对应resolve,reject,且同时只能有一个
    if (this.status === Pro.fulfilled) {
let pro = new Pro((resolve, reject) => {
  console.log(2);
  // 加个定时器查看当resolve函数也成异步时,then会怎么执行
  setTimeout(() => {
    console.log(2,pro.status);
    resolve('myPro')
    console.log(3,pro.status);
    console.log(4);
  })
})

结果如下

在这里插入图片描述

可以发现then执行判断状态时,实例的resolve还没执行所以状态为待定,而我此时then里没有对状态为待定时进行处理,由于resolve处于异步队列还未执行,所以我先将then函数参数onFulfilled暂存在数组里,等待resolve函数触发时再取出来循环执行

   // 保留then里的函数,保证其在状态改变时能执行
    this.resolveArr = []
    this.rejectArr = []
    fn(this.resolve.bind(this), this.reject.bind(this))
    // 当状态为待定时
    if (this.status === Pro.PENDING) {
      this.resolveArr.push(onFulfilled)
      this.rejectArr.push(onRejected)
    }

再次执行

在这里插入图片描述

此时发现虽然resolve执行了,但是执行的顺序不对,应该为异步执行,即排在 4 后执行 所以我在resolve里最外层再加个定时器进入异步队列

  resolve(res) {
    // 异步执行
    setTimeout(() => {
      // 如果状态为待定则赋值成功
      if (this.status === Pro.PENDING) {
        this.status = Pro.fulfilled
        // 赋值结果
        this.result = res
        // 如果数组有暂存的then函数,循环执行
        this.resolveArr.forEach(cb => {
          cb(res)
        })
      }
    })
  }

执行,这次顺序对了

在这里插入图片描述

promise的链式调用

先测试

pro.then(
  (res) => {
    console.log(res);
  },
  (rejRes) => {
    console.log(rejRes);
  },
).then(
  (res) => {
    console.log(res);
  },
  (rejRes) => {
    console.log(rejRes);
  },
)

在这里插入图片描述

果然报错,原因是.then的对象得是个promise实例,所以我们在.then函数调用后返回一个新的promise实例供下个then调用

  // .then回调
  then(onFulfilled, onRejected) {
      // 供下个then链式调用
    return new Pro((resolve, reject) => {
      // 当状态为待定时
      if (this.status === Pro.PENDING) {
        this.resolveArr.push(onFulfilled)
        this.rejectArr.push(onRejected)
      }
      // 两个函数参数分别对应resolve,reject,且同时只能有一个
      if (this.status === Pro.fulfilled) {
        // resolve 执行并返回结果
        // 定时器进行异步
        setTimeout(() => {
          onFulfilled(this.result)
        })
      }
      // 和resovle对应,暂时放空
      if (this.status === Pro.rejected) {
        // reject 执行并返回结果
        onRejected(this.result)
      }
    })
  }

执行没报错,成功!

今天暂时就写到这,这个小仿写还有很多bug和没做的东西,后续继续添加和完善

作者:徐煜&煎包