从0到1实现Promise

1,345 阅读5分钟

前言

前言:Promise的出现为解决异步和毁掉地狱问题作出了巨大贡献,但是仅仅停留在会用的层面是不行的,只有深入其本质,我们才能体会到它的神秘之处。本文我就来介绍如何从0到1实现一个符合Promise A+规范的Promise,以及Promise的一些静态方法。

Promise A+ 规范详见promisesaplus.com/

Promise的实现

初步实现可以修改状态并且保留结果的Promise

  • 要点1:executor类型检测

由下图可以看出,当executor不是Function类型的时候,会抛出TypeError的类型错误。

image.png

  • 要点2:Promise构造函数要求传入一个函数,这个函数传入两个函数类型的参数(resolve,reject)用于改变Promise的状态和值。这个函数会立即执行,Promise的状态只能由pendding(等待)变成fulfilled(成功)和rejected(失败) 并且只能改变一次

具体实现如下

class Promise{
  constructor(executor) {
    //不能相信用户的输入,参数校验
    if(typeof executor !== 'function'){
      throw new TypeError(`Promise resolver ${executor} is not a function`)
    }
    //初始化值
    this.value = null    //终值
    this.reason = null    //拒因
    this.state = 'pending'   //状态

    const resolve = value => {
      //状态只能从 pending => 其他,所以需要判断
      if(this.state === 'pending'){
        this.state = Promise.FULFILLED
        this.value = value
      }
    }

    const reject = reason => {
      if(this.state === 'pending'){
        this.state = Promise.REJECTED
        this.reason = reason
      }
    }
    //立即执行传入的executor
    executor(resolve,reject)
  }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'rejected'

then方法初步实现

then方法可以传入两个函数类型的参数:onFulfilledonRejected,这两个参数是对应Promise状态改变后要执行的回调函数,在执行时会传入对应的Promise的值

  • 要点1:判断onFulfilledonRejected是否是函数类型,不是函数类型(或者没传,undefined)就赋一个默认回调来达到链式调用的穿透(传递)效果
  • 要点2:传入的回调只有状态改变后才能调用(状态为pending的时候不能调用)
class Promise{
  constructor(executor) {
    if(typeof executor !== 'function'){
      throw new TypeError(`Promise resolver ${executor} is not a function`)
    }
    this.value = null  
    this.reason = null    
    this.state = Promise.PENDING   

    const resolve = value => {
      if(this.state === Promise.PENDING){
        this.state = Promise.FULFILLED
        this.value = value
      }
    }

    const reject = reason => {
      if(this.state === Promise.PENDING){
        this.state = Promise.REJECTED
        this.reason = reason
      }
    }
    executor(resolve,reject)
  }
  then(onFulfilled,onRejected){
    //参数校验
    if(typeof onFulfilled !== 'function'){
      onFulfilled = value => value
    }
    if(typeof onRejected !== 'function'){
      onRejected = reason => {throw reason}
    }
    
    //不能在状态改变前调用
    if(this.state === Promise.FULFILLED){
      onFulfilled(this.value)
    }
    //不能在状态改变前调用
    if(this.state === Promise.REJECTED){
      onRejected(this.reason)
    }
  }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'rejected'

异步修改Promise状态

  • 要点1:原版promise的then方法中的回调是异步执行的,验证如下,所以实现的Promise的回调也要异步执行
console.log(1)
const p = new Promise((resolve,reject)=>{
    console.log(2)
    resolve()
})
p.then(value=>{
    console.log(4)
},err=>{
    console.log(err)
})
console.log(3)
​
//这段代码的输出结果是1,2,3,4(原版Promise)
//但是放到上面一个小节中,输出结果是1,2,4,3,也就是说,then中的回调是立即执行了
  • 要点2:在构造函数中的executor执行外面加try...catch解决在executor中抛出异常的问题
  • 要点3:在then中添加pending状态的判断,将回调存入数组等到状态改变的时候执行,解决在executor中执行异步 ,延迟修改状态导致then中的回调函数没有执行的情况
class Promise{
  constructor(executor) {
    if(typeof executor !== 'function'){
      throw new TypeError(`Promise resolver ${executor} is not a function`)
    }
    this.value = null
    this.reason = null
    this.state = Promise.PENDING
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = value => {
      if(this.state === Promise.PENDING){
        this.state = Promise.FULFILLED
        this.value = value
        //状态修改后执行(针对pending状态时存入的回调)
        this.onFulfilledCallbacks.forEach(fn=>fn())
      }
    }

    const reject = reason => {
      if(this.state === Promise.PENDING){
        this.state = Promise.REJECTED
        this.reason = reason
        //状态修改后执行(针对pending状态时存入的回调)
        this.onRejectedCallbacks.forEach(fn=>fn())
      }
    }
	//捕获executor中的异常
    try {
      executor(resolve,reject)
    }catch (e) {
      reject(e)
    }

  }
  then(onFulfilled,onRejected){
    if(typeof onFulfilled !== 'function'){
      onFulfilled = value => value
    }
    if(typeof onRejected !== 'function'){
      onRejected = reason => {throw reason}
    }

    if(this.state === Promise.FULFILLED){
      //将回调异步执行
      setTimeout(()=>{
        onFulfilled(this.value)
      })
    }

    if(this.state === Promise.REJECTED){
        //将回调异步执行
      setTimeout(()=>{
        onRejected(this.reason)
      })
    }
	
    //executor中异步修改状态,此时执行then的时候是pending状态,需要保存要执行的回调函数,等到状态修改后执行
    if(this.state === Promise.PENDING){
      this.onFulfilledCallbacks.push(()=>{
          //将回调异步执行
        setTimeout(()=>{
          onFulfilled(this.value)
        })
      })
      this.onRejectedCallbacks.push(()=>{
          //将回调异步执行
        setTimeout(()=>{
          onRejected(this.reason)
        })
      })
    }
  }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'rejected'

module.exports = Promise

链式调用实现

  • then返回值是一个新的promise

image.png

  • promise解决过程

    • 如果x === promise2
    • 如果x instanceof Promise
    • 如果 x是object || x是function

image.png

  • 如果then回调的返回值x是promise,并且这个promise的resolve中又返回一个promise,那么promise2的结果由这个resolve中的promise决定。(状态移交)

image.png

class Promise{
  constructor(executor) {
    if(typeof executor !== 'function'){
      throw new TypeError(`Promise resolver ${executor} is not a function`)
    }
    this.value = null
    this.reason = null
    this.state = Promise.PENDING
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = value => {
      if(this.state === Promise.PENDING){
        this.state = Promise.FULFILLED
        this.value = value
        this.onFulfilledCallbacks.forEach(fn=>fn())
      }
    }

    const reject = reason => {
      if(this.state === Promise.PENDING){
        this.state = Promise.REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn=>fn())
      }
    }

    try {
      executor(resolve,reject)
    }catch (e) {
      reject(e)
    }

  }
  then(onFulfilled,onRejected){
    //如果onFulfilled不是函数,且promise1成功执行,promise2必须返回相同的终值(状态为fulfilled)
    if(typeof onFulfilled !== 'function'){
      onFulfilled = value => value
    }
    //如果onRejected不是函数,且promise1拒绝执行,promise2必须返回相同的拒绝原因(状态为rejected)
    if(typeof onRejected !== 'function'){
      onRejected = reason => {throw reason}
    }
    
    //返回值是Promise
    let promise2 = new Promise((resolve,reject)=>{

      if(this.state === Promise.FULFILLED){
        setTimeout(()=>{
          //如果onFulfilled或onRejected执行时抛出一个异常e,则返回的promise必须拒绝执行,并返回拒因e
          try{
            //如果有返回值,则执行promise解决过程
            const x = onFulfilled(this.value)
            Promise.resolvePromise(promise2, x, resolve, reject)
          }catch (e){
            reject(e)
          }
        })
      }

      if(this.state === Promise.REJECTED){
        setTimeout(()=>{
          try {
            const x = onRejected(this.reason)
            Promise.resolvePromise(promise2, x, resolve, reject)
          }catch (e) {
            reject(e)
          }
        })
      }

      if(this.state === Promise.PENDING){
        this.onFulfilledCallbacks.push(()=>{
          setTimeout(()=>{
            try {
              const x = onFulfilled(this.value)
              Promise.resolvePromise(promise2, x, resolve, reject)
            }catch (e){
              reject(e)
            }
          })
        })
        this.onRejectedCallbacks.push(()=>{
          setTimeout(()=>{
            try {
              const x = onRejected(this.reason)
              Promise.resolvePromise(promise2, x, resolve, reject)
            }catch (e){
              reject(e)
            }
          })
        })
      }
    })
    return promise2
  }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'rejected'
Promise.resolvePromise = function(promise2,x,resolve,reject){

  //如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise(形成了链式调用)
  if(promise2 === x){
    reject(new TypeError('Chaining cycle detected for promise'))
  }
    
  //标记如果x是对象或函数且有then方法时,回调只能调一次,避免出现{then(a,b){ a() ;a() }}多次调用,多次执行的情况
  let flag = false
  //如果返回值x是promise类型,那么then返回的promise的类型与这个promise的执行结果一致
  if(x instanceof Promise){
    //什么时候执行结束?执行了x.then中的回调的时候说明promise(x)中的executor执行结束了
    x.then(value=>{
      //这里不应该简单的执行resolve,因为如果x的resolve的参数是一个promise对象的话,promise2的结果由这个参数决定
      Promise.resolvePromise(promise2,value,resolve,reject)
    },reason=>{
      reject(reason)
    })
  }
  //如果x是对象或者是函数,注意:null也是object类型,所以要特判不能为null
  else if(x !== null && (typeof x === 'object' || typeof x === 'function')){
    //可能x对象的then方法中会抛出异常
    try{
      //规范要求我们先将x.then存储到then中,避免重复从对象中取值
      const then = x.then
      //判断是不是thenable对象(有then方法的对象)
      if(typeof then === 'function'){
        then.call(
            x,
            value => {
              if(flag) return
              flag = true
              Promise.resolvePromise(promise2,value,resolve,reject)
            },
            reason =>{
              if(flag) return
              flag = true
              reject(reason)
            })
      }else{
        if(flag) return
        flag = true
        //不是thenable对象的话,就是普通对象,直接resolve
        resolve(x)
      }
    }catch (e) {
      if(flag) return
      flag = true
      reject(e)
    }
  }
    //是普通值
  else{
    resolve(x)
  }
}

Promise的静态方法实现

其实Promise的静态方法,无非就是取决于何时调用resolve和reject改变返回的Promise状态。下面我们来一个个实现。

Promise.all

Promise.all方法是,传入的所有Promise都成功,返回成功的状态以及这些成功的值组成的数组,有一个失败就直接返回失败的结果

Promise.prototype.all = function (arr) {
  return new Promise((resolve, reject) => {
    let res = [];
    //遍历数组,给每个promise对象指定then回调
    for (const promise of arr) {
      promise.then((data) => {
        res.push(data);
        //所有promise都为成功状态时,改变返回的Promise的状态
        if (res.length === arr.length) resolve(res);
      }, reject);
    }
  });
};

Promise.allSettled

Promise.allSettled方法是,传入的所有Promise状态都改变了,才改变返回的Promise的状态,并且返回的Promise中保存的是所有传入的Promise的状态和值组成的数组。

Promise.prototype.allSettled = function (arr) {
  return new Promise((resolve, reject) => {
    let res = [];
    //遍历数组,给每个promise对象指定then回调
    for (const promise of arr) {
      promise.then(
        (data) => {
          res.push({ value: data, status: Promise.FULFILLED });
          //所有promise状态都改变时,改变返回的Promise的状态
          if (res.length === arr.length) resolve(res);
        },
        (err) => {
          res.push({ value: err, status: Promise.REJECTED });
          //所有promise状态都改变时,改变返回的Promise的状态
          if (res.length === arr.length) reject(res);
        }
      );
    }
  });
};

Promise.race

Promise.race方法是,传入一个Promise数组,返回的Promise的状态取决于第一个改变状态的promise对象

Promise.prototype.race = function (arr) {
  return new Promise((resolve, reject) => {
    for (const promise of arr) {
      //当某个promise状态修改的时候,直接修改返回的promise的状态
      promise.then(resolve,reject);
    }
  });
};

Promise.any

Promise.any方法是,传入一个Promise数组,必须等到一个Promise成功的结果才改变返回的Promise的状态为成功,否则只有当所有Promise都为失败状态时,才改变返回的Promise的状态为失败,并且保存所有失败原因。

Promise.prototype.any = function (arr) {
  return new Promise((resolve, reject) => {
    let res = [];
    //遍历数组,给每个promise对象指定then回调
    for (const promise of arr) {
      promise.then(resolve, (err) => {
        res.push(err);
        //所有promise都为失败状态时,改变返回的Promise的状态
        if (res.length === arr.length) reject(res);
      });
    }
  });
};