200 行逐行分解 Promise 模拟实现(ES6)

704 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

再看不懂你就来砍我吧

// 如何快速写一个 Promise
var MyPromise = (function() {
  // 1. 三种状态
  var PENDING = 'pending';
  var FULFILLED = 'fulfilled';
  var REJECTED = 'rejected';
  /**
   * 2. `Promise constructor` 是一个函数,通过 `new` 操作符调用,接受一个立即执行的函数作为参数
   * @param {function} executor 
   * @returns Promise
   */
  function _Promise(executor){
    var p = this
    // 3. 初始化状态,成功,失败异步队列
    p.status = PENDING
    p.value = null
    p.reason = null
    p.onFulfilled = []
    p.onRejected = []
    try {
      // 4. 立即执行这个函数,并给这个函数传入两个入参(resolve, reject)用于改变 promise 状态
      executor(function(value){
        // 5. resolvePromise 通过此方法接着解决 promise
        resolvePromise(p, value)
      }, function(reason){
        rejectPromise(p, reason)
      })
    } catch (error) {
      rejectPromise(p, error)
    }
    // 返回一个 Promise 实例
    return p
  }
  /**
   * 6. then 方法接受两个回调方法,并返回一个新的 promise 对象
   * @param {function} onFulfilled 成功回调
   * @param {function} onRejected 失败回调
   * @returns promise
   */
  _Promise.prototype.then = function(onFulfilled, onRejected){
    // 7. 需要对回调方法进行兼容
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value){ return value }
    onRejected = typeof onRejected === 'function' ? onRejected : function(reason){ throw reason }
    // 8. 当前的 `promise` 实例
    var p1 = this
    // 9. 返回一个新的 `promise` 实例
    var p2 = new MyPromise(function(){})
    // 10. 注册微任务事件
    function fulfilled(){
      // 注册一个微任务
      queueMicrotask(function(){
        try {
          var x = onFulfilled(p1.value)
          // 这里得注意要使用 p2
          resolvePromise(p2, x)
        } catch (error) {
          rejectPromise(p2, error)
        }
      })
    }
    function rejected(){
      queueMicrotask(function(){
        try {
          var x = onRejected(p1.reason)
          resolvePromise(p2, x)
        } catch (error) {
          rejectPromise(p2, error)
        }
      })
    }
    // 判断状态
    var status = p1.status
    if(status === PENDING){
      // 推入队列中
      p1.onFulfilled.push(fulfilled)
      p1.onRejected.push(rejected)
    }else if(status === FULFILLED){
      // 立即执行
      fulfilled()
    }else if(status === REJECTED){
      // 立即执行
      rejected()
    }
    // 返回一个新的 promise 实例
    return p2
  }
  /**
   * 15. 补一个 runCb 方法执行回调函数
   * @param {function[]} cbs 
   * @param {any} val 
   */
  function runCbs(cbs, val){
    cbs.forEach(function(cb){
      cb(val)
    })
  }
  // 11. 接着来写三个处理promise状态的函数
  /**
   * 16. 失败的 promise 处理方法
   * @param {promise} p promise 实例
   * @param {any} reason 失败原因
   * @returns 
   */
  function rejectPromise(p, reason){
    // 12. rejected 的时候得这么做呢?首先判断当前状态是否为 PENDING
    if(p.status !== PENDING) return
    // 13. 更改状态,保存 reason
    p.status = REJECTED
    p.reason = reason
    // 14. 依次执行 onRejected 中的回调方法
    runCbs(p.onRejected, p.reason)
  }
  /**
   * 17. 成功的 promise 处理方法
   * @param {promise} p promise 实例
   * @param {any} value 
   */
  function fulfillPromise(p, value){
    // 18. 同样的,判断状态,更改状态,保存值,执行函数
    if(p.status !== PENDING) return
    p.status = FULFILLED
    p.value = value
    runCbs(p.onFulfilled, p.value)
  }
  /**
   * promise resolve  程序,核心
   * @param {promise} p 需要被解决的 promise 实例
   * @param {any} x 
   */
  function resolvePromise(p, x){
    // 19. 禁止递归调用
    if(p === x) return rejectPromise(p, new TypeError('same'))
    // 20. 如果 x 是一个 promise 实例的情况
    if(x instanceof MyPromise){
      // 21. 如果 x 还是一个等待中的状态,往 then 增加回调处理
      if(x.status === PENDING){
        x.then(function(value){
          resolvePromise(p, value)
        }, function(reason){
          rejectPromise(p, reason)
        })
      }else if(x.status === FULFILLED){
        // 22. 如果是一个已成功的状态,直接使用成功的处理方法
        fulfillPromise(p, x.value)
      }else if(x.status === REJECTED){
        // 23. 如果是一个已失败的状态,直接使用失败的处理方法
        rejectPromise(p, x.reason)
      }
    }else if(x && (typeof x === 'function' || typeof x === 'object')){
      // 25. 这里是处理 thenable 的情况
      var then
      try {
        then = x.then
      } catch (error) {
        // 26. 报错则直接失败
        rejectPromise(p, error)
      }
      if(typeof then === 'function'){
        // 这里需要只调用一次
        var done = false
        try {
          // 调用 then
          then.call(x, function(y){
            if(done) return
            done = true
            // 接着解决
            resolvePromise(p, y)
          }, function(reason){
            if(done) return
            done = true
            rejectPromise(p, reason)
          })
        } catch (error) {
          if(done) return
          done = true
          rejectPromise(p, error)
        }
      }else{
        // 27. 如果不是一个函数,则直接成功
        fulfillPromise(p, x)
      }
    }else{
      // 24. 其他情况都算成功
      fulfillPromise(p, x)
    }
  }
  return _Promise
})();

// npm i --save promises-aplus-tests && promises-aplus-tests index.js
// 就可以测试是否符合 Promise A+ 规范
MyPromise.deferred = function(){
  var result = {}
  result.promise = new MyPromise(function(resolve, reject){
    result.resolve = resolve
    result.reject = reject
  })
  return result
}

module.exports = MyPromise

image.png

关于 then 方法的规范

image.png

流转图

image.png

再贴一个 ES6 的实现

code.png

源码地址