Promise源码

361 阅读9分钟

手写一个Promise源码

一步一步实现Promise的过程。 首先实现一个最简单的promise

class MyPromise {
  // 立即执行executor函数
  constructor(executor) {
    executor(this.resolve, this.reject)
  }
  onFulfilledCallbacks = [] // 定义一个成功的回调数组
  onRejectedCallbacks = [] // 定义一个失败的回调数组
  status = 'PENDING' // 定义一个状态,根据Promises/A+规范,状态一旦改变不可再改
  value = null // 状态变为FULLFULED时,当做.then中的成功回调onFulfilled函数的参数(值)
  reason = null // 状态变为REJECTED时,当做.then中的失败回调onRejected函数的参数(原因)
  // 当做立即执行函数executor的第一个参数,一般为触发.then成功回调的作用
  resolve = (value) => {
    if(this.status === 'PENDING') {
      // 如果状态为'等待'状态时,先将状态修改为'成功'
      this.status = 'FULFILLED'
      // 提供给.then成功回调的参数
      this.value = value
      // 如果是异步调用resolve的情况,this.onFulfilledCallbacks会在.then方法里收集传入的onFulfulled函数
      this.onFulfilledCallbacks.forEach(fn => fn(value))
    }
  }
  reject = (reason) => {
    if(this.status === 'PENDING') {
    // 如果状态为'等待'状态时,先将状态修改为'失败'
      this.status = 'REJECTED'
      // 提供给.then失败回调的参数(原因)
      this.reason = reason
      // 如果是异步调用reject的情况,this.onRejectedCallbacks会在.then方法里收集传入的onRejected函数
      this.onRejectedCallbacks.forEach(fn => fn(reason))
    }
  }
  then = function(onFulfilled, onRejected) {
    if(this.status === 'PENDING') {
      // 状态为等待状态时,进入这里说明前面new MyPromise(resolve, reject)中是通过异步的方式调用resolve或reject的。
      this.onFulfilledCallbacks.push((value) => { onFulfilled(value) })
      this.onRejectedCallbacks.push((reason) => { onRejected(reason) })
    }
    if(this.status === 'FULFILLED') {
      // 说明前面的new MyPromise(resolve, reject)中是通过同步的方式调用resolve的。
      onFulfilled(this.value)
    }
    if(this.status === 'REJECTED') {
      // 说明前面的new MyPromise(resolve, reject)中是通过同步的方式调用reject的。
      onRejected(this.reason)
    }
  }
}

这样一个最简单的Promose外壳就完成了。

那接下来我们将去实现.then的链式调用。如何链式调用?可想而知,无非就是.then的时候返回一个新的 Promise实例嘛,接下来让我们在原来的基础上来实现一下。

    then(onFulfilled, onRejected) {
      const promise2 = new MyPromise((resolve, reject) => {
        if(this.status === 'FULFILLED') {
          onFulfilled(this.value)
        } else if(this.status === 'REJECTED') {
          onRejected(this.reason)
        } else if(this.status === 'PENDING') {
          this.onFulfilledCallbacks.push((value) => { onFulfilled(value) })
          this.onRejectedCallbacks.push((reason) => { onRejected(reason) })
        }
      })
      // 将新的Promise返回,给后续的.then调用
      return promise2
    }

这样链式调用.then().then()就不会报错了,但是我们现在并没办法将上一个.then中resolve或者reject函数的返回值传递给下一个.then里,那该如何解决这个问题,其实很简单,我们要明白.then是谁的then函数,指向谁,谁调用指向谁,所以就是上一个.then的返回的那个Promise实例,那接下来怎么操作就很明显了。将上面的then函数进行修改。

  then(onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      if(this.status === 'FULFILLED') {
        let value = onFulfilled(this.value)
        resolve(value)
      } else if(this.status === 'REJECTED') {
        let reason = onRejected(this.reason)
        reject(reason)
      } else if(this.status === 'PENDING') {
        this.onFulfilledCallbacks.push((value) => { onFulfilled(value) })
        this.onRejectedCallbacks.push((reason) => { onRejected(reason) })
      }
    })
    // 将新的Promise返回,给后续的.then调用
    return promise2
  }

让我们测试一下

new MyPromise(resolve => {
  resolve(1)
}).then(value => {
  console.log(value)
  return value + 1
}).then(value => {
  console.log(value)
})
输出:1 2

ok,Promise/A+又规定了.then()可以不传参数哇,那我们该如何将上一个.then里面resolve或者reject返回值传递给当前.then的下一个.then呢,也就是如何去做值穿透呢?请看下面代码。

  then(onFulfilled, onRejected) {
    // 如果onFulfilled不是一个函数的话,我们就将其转为一个函数,并返回上一个promise实例传递过来的值,传递什么返回什么,就能做到值穿透
    const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    // 如果onRejected不是一个函数的话,我们就将其转为一个函数,作用同realOnFulfilled类似
    const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {
      throw reason
    }
    const promise2 = new MyPromise((resolve, reject) => {
      if(this.status === 'FULFILLED') {
        let value = realOnFulfilled(this.value)
        resolve(value)
      } else if(this.status === 'REJECTED') {
        let reason = realOnRejected(this.reason)
        reject(reason)
      } else if(this.status === 'PENDING') {
        this.onFulfilledCallbacks.push((value) => { realOnFulfilled(value) })
        this.onRejectedCallbacks.push((reason) => { realOnRejected(reason) })
      }
    })
    // 将新的Promise返回,给后续的.then调用
    return promise2
  }

现在测试一下这个功能

new MyPromise(resolve => {
  resolve(1)
}).then().then(value => {
  console.log(value)
})

ok,假如现在.then中resolve的是一个Promise实例如何解决。现在的版本很明显不适用。那我们又该如何修改?

  then(onFulfilled, onRejected) {
    // 如果onFulfilled不是一个函数的话,我们就将其转为一个函数,并返回上一个promise实例传递过来的值,传递什么返回什么,就能做到值穿透
    const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    // 如果onRejected不是一个函数的话,我们就将其转为一个函数,作用同realOnFulfilled类似
    const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {
      throw reason
    }
    const promise2 = new MyPromise((resolve, reject) => {
      if(this.status === 'FULFILLED') {
        // 如果这里x的返回值是Promise实例
        let x = realOnFulfilled(this.value)
        // 那我们就定义一个resolvePromise函数,其实目的和之前的一样,无非就是调用reslove(参数),只是需要对返回值进行不同的处理
        resolvePromise(x, resolve, reject)
      } else if(this.status === 'REJECTED') {
        // 如果这里x的返回值是Promise实例,同FULFILLED
        let x = realOnRejected(this.reason)
        resolvePromise(x, resolve, reject)
      } else if(this.status === 'PENDING') {
        this.onFulfilledCallbacks.push((value) => { realOnFulfilled(value) })
        this.onRejectedCallbacks.push((reason) => { realOnRejected(reason) })
      }
    })
    // 将新的Promise返回,给后续的.then调用
    return promise2
  }
  
  function resolvePromise(x, resolve, reject) {
    if (typeof x === 'object' || typeof x === 'function') {
      if(x === null) {
        return resolve(x)
      }
      let then
      try {
        then = x.then
      } catch(err) {
        reject(err)
      }
      if (then === 'function') {
        try {
          then.call(
            x,
            y => {
              // 递归调用resolvePromise函数,假如返回的Promise实例里面又resolve一个Promise实例
              resolvePromise(y, resolve, reject)
            },
            r => {
              reject(r)
            }
          )
        } catch(err) {
        
        }
      } else {
        // 如果是对象值,就直接resolve调用传递就可以了
        resolve(x)
      }
    } else {
      // 如果是普通值,就直接resolve调用传递就可以了
      resolve(x)
    }
  }

测试一下

  new MyPromise(resolve => {
    resolve(1)
  }).then(value => {
    return new MyPromise(resolve => {
      resolve(value + 1)
    })
  }).then(value => {
    console.log(value)
  })

ok,成功解决。假如现在我们有代码

  let p1 = new MyPromise(resolve => {
    resolve(1)
  })
  p1.then(value => {
    return p1
  })

这样就会报类型错误Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>,这个时候我们就应该去检查.then里面的onFulfilled的返回值是否跟当前的Promise实例相等,修改下源代码

  then(onFulfilled, onRejected) {
    // 如果onFulfilled不是一个函数的话,我们就将其转为一个函数,并返回上一个promise实例传递过来的值,传递什么返回什么,就能做到值穿透
    const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    // 如果onRejected不是一个函数的话,我们就将其转为一个函数,作用同realOnFulfilled类似
    const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {
      throw reason
    }
    const promise2 = new MyPromise((resolve, reject) => {
      if(this.status === 'FULFILLED') {
        // 添加一个queueMicrotask微任务,让读取promise2进入下一个微任务,避免读取不到promise2而报错
        queueMicrotask(() => {
          // 如果这里x的返回值是Promise实例
          let x = realOnFulfilled(this.value)
          // 那我们就定义一个resolvePromise函数,其实目的和之前的一样,无非就是调用reslove(参数),只是需要对返回值进行不同的处理
          resolvePromise(promise2, x, resolve, reject)
        })
      } else if(this.status === 'REJECTED') {
        // 添加一个queueMicrotask微任务,让读取promise2进入下一个微任务,避免读取不到promise2而报错
        queueMicrotask(() => {
          try {
            // 如果这里x的返回值是Promise实例,同FULFILLED
            const x = realOnRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          } 
        }) 
      } else if(this.status === 'PENDING') {
        this.onFulfilledCallbacks.push((value) => { realOnFulfilled(value) })
        this.onRejectedCallbacks.push((reason) => { realOnRejected(reason) })
      }
    })
    // 将新的Promise返回,给后续的.then调用
    return promise2
  }
  
  function resolvePromise(promise, x, resolve, reject) {
    // 相同reject错误
    if (promise === x) {
      return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (typeof x === 'object' || typeof x === 'function') {
      if(x === null) {
        return resolve(x)
      }
      let then
      try {
        then = x.then
      } catch(err) {
        reject(err)
      }
      if (then === 'function') {
        try {
          then.call(
            x,
            y => {
              // 递归调用resolvePromise函数,假如返回的Promise实例里面又resolve一个Promise实例
              resolvePromise(promise, y, resolve, reject)
            },
            r => {
              reject(r)
            }
          )
        } catch(err) {
          reject(err);
        }
      } else {
        // 如果是对象值,就直接resolve调用传递就可以了
        resolve(x)
      }
    } else {
      // 如果是普通值,就直接resolve调用传递就可以了
      resolve(x)
    }
  }

ok,没什么问题,最后贴上完整代码

  class MyPromise {
    constructor(executor) {
      try {
        executor(this.resolve, this.reject)    
      } catch(err) {
        throw err
      }
    }
    onFulfilledCallbacks = []
    onRejectedCallbacks = []
    status = 'PENDING'
    value = null
    reason = null 
    resolve = (value) => {
      if(this.status === 'PENDING') {
        this.status = 'FULFILLED'
        this.value = value
        this.onFulfilledCallbacks.forEach(fn => fn(value))
      }
    }
    reject = (reason) => {
      if(this.status === 'PENDING') {
        this.status = 'REJECTED'
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn(reason))
      }
    }
    then(onFulfilled, onRejected) {
      // 如果onFulfilled不是一个函数的话,我们就将其转为一个函数,并返回上一个promise实例传递过来的值,传递什么返回什么,就能做到值穿透
      const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
      // 如果onRejected不是一个函数的话,我们就将其转为一个函数,作用同realOnFulfilled类似
      const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {
        throw reason
      }
      const promise2 = new MyPromise((resolve, reject) => {
        const fulfilledMicrotask = () =>  {
          // 添加一个queueMicrotask微任务,让读取promise2进入下一个微任务,避免读取不到promise2而报错
          queueMicrotask(() => {
            try{
              // 如果这里x的返回值是Promise实例
              let x = realOnFulfilled(this.value)
              // 那我们就定义一个resolvePromise函数,其实目的和之前的一样,无非就是调用reslove(参数),只是需要对返回值进行不同的处理
              resolvePromise(promise2, x, resolve, reject)
            } catch(error) {
              reject(error)
            }
          })
        }
        const rejectedMicrotask = () => {
          // 添加一个queueMicrotask微任务,让读取promise2进入下一个微任务,避免读取不到promise2而报错
          queueMicrotask(() => {
            try {
              // 如果这里x的返回值是Promise实例,同FULFILLED
              const x = realOnRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error)
            }
          }) 
        }
        if(this.status === 'FULFILLED') {
          fulfilledMicrotask()
        } else if(this.status === 'REJECTED') {
          rejectedMicrotask()
        } else if(this.status === 'PENDING') {
          this.onFulfilledCallbacks.push(fulfilledMicrotask)
          this.onRejectedCallbacks.push(rejectedMicrotask)
        }
      })
      // 将新的Promise返回,给后续的.then调用
      return promise2
    }
    catch (onRejected) {
      // 只需要进行错误处理
      this.then(undefined, onRejected);
    }
  }
    
  function resolvePromise(promise, x, resolve, reject) {
    // 相同reject错误
    if (promise === x) {
      return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (typeof x === 'object' || typeof x === 'function') {
      if(x === null) {
        return resolve(x)
      }
      let then
      try {
        then = x.then
      } catch(err) {
        reject(err)
      }
      if (typeof then === 'function') {
        let called = false;
        try {
          then.call(
            x,
            y => {
              if (called) return;
              called = true;
              // 递归调用resolvePromise函数,假如返回的Promise实例里面又resolve一个Promise实例
              resolvePromise(promise, y, resolve, reject)
            },
            r => {
              if (called) return;
              called = true;
              reject(r)
            }
          )
        } catch(err) {
          if (called) return;
          reject(err);
        }
      } else {
        // 如果是对象值,就直接resolve调用传递就可以了
        resolve(x)
      }
    } else {
      // 如果是普通值,就直接resolve调用传递就可以了
      resolve(x)
    }
  }
  MyPromise.deferred = function () {
    var result = {};
    result.promise = new MyPromise(function (resolve, reject) {
      result.resolve = resolve;
      result.reject = reject;
    });
    return result;
  }
  module.exports = MyPromise

检查Promise/A+规范,需要新建一个package.json

  // package.json
  {
    "name": "promise",
    "version": "1.0.0",
    "description": "my promise",
    "main": "promise.js",
    "scripts": {
        "test": "promises-aplus-tests promise"
    },
    "author": "Wayag",
    "license": "ISC",
    "devDependencies": {
        "promises-aplus-tests": "^2.1.2"
    }
  }

运行npm run test。

既然讲了Promise源码,那顺便讲一下generator的自动化执行,也async await的简单实现,先来看下generator的手动执行。

function* myGenerator() {
    console.log(yield Promise.resolve(1)) // 1
    console.log(yield Promise.resolve(2)) // 2
    console.log(yield Promise.resolve(3)) // 3
}
const gen = myGenerator()
gen.next().value.then(val => {
  console.log(val) // 1
  gen.next(val).value.then(val => {
    console.log(val) // 2
    gen.next(val).value.then(val => {
      console.log(val) // 3
      gen.next(val)
    })
  })
})

接下来就是把手动执行的部分换成自动执行,采用递归的方式我们很快就能完成。

function run (generator) {
  let g = generator()
  function _next (val) {
    let res = g.next(val)
    if (res.done) return res.value
    Promise.resolve(res.value).then(_next ,reject)
  }
  _next()
}
run(myGenerator)

根据async await最后应该返回一个Promise实例我们需要对源码进行修改

function run (generator) {
  return new Promise((resolve, reject) => {
    let g = generator()
    function _next (val) {
      let res = g.next(val)
      if (res.done) return resolve(res.value)
      Promise.resolve(res.value).then(_next ,reject)
    }
    _next()
  })
}