记阅读promise-polyfill源码探究Promise运行原理

Promise可以看作一个异步操作的占位符,表示一个异步操作的最终状态(完成或失败),以及其返回的值,是目前流行Javascript异步编程解决方案之一。

网上关于Promise用法的文章汗牛充栋,作者在本文也不赘述了。Promise用久了难免会对其内部运行原理感到好奇,作者曾试着遵循Promise/A+规范自己写一个promise的补丁,无奈野心被能力实力打脸,冷静之后 去github上找promise的polyfill学习观摩,promise-polyfill作为一个轻量级、设计巧妙的polyfill让作者大开眼界,仔细观摩学习之后,写成本篇小记与大家分享,错漏之处恳求指点。

一 .框架

作者将不会按照promise-polyfill的源码一行行进行讲解,而为了阅读方便将使用ES6/7的语法重新搭好一个框架,慢慢往里面塞promise-polyfill核心代码

我们将搭好下面的代码架构,先实现除了constructor、then的其他方法找找感觉,然后慢慢往里加promise-polyfill的核心代码,不耐烦的同学可以直接跳到下文 核心 部分阅读

class Promise {
    constructor (executor) { ... }
    then (onFulfilled, onFulfilled) { ... }
    catch (onRejected) { ... }
    finally(callback) { ... }
    static resolve(value) { ... }
    static reject(reason) { ... }
    static all(iterable) { ... }
    static race(iterable) { ... }
}
复制代码

先假装已经实现了 constructor、then 的核心功能

1. catch

/**
*  catch很简单,就是then(null, onRejected)的语法糖
*/

catch (onRejected) {
    return this.then(null, onRejected)
}

复制代码

2.finally

/***
**  finally总是执行回调
***/
finally (callback) {
    return this.then(
        function(value) {
            return Promise.resolve(callback()).then(() => value)
        },
        function(reason) {
            return Promise.resolve(callback()).then(() => Promise.reject(reason))
        }
    )
}

// tips

prom.finally(() => { «statements» })

等于

prom.then(result => {
    «statements»
    return result
}, error => {
    «statements»
    throw error
})
复制代码

3. resolve

/**
*  静态resolve方法返回一个Promise对象
*/
static resolve (value) {
  if (value && typeof value === 'object' && value.constructor === Promise) {
    return value  
  }
  return new Promise(function(resolve, reject) {
    resolve(value)
  })
}

复制代码

4. reject

  static reject (reason) {
    return new Promise(function (resolve, reject) {
      reject(reason)
    })
  }
复制代码

5. all

/**
**** 简单起见,并未按照promise-polyfill的all方法,面试中也经常出现要求实现一个Promise.all方法
**/
static all (iterable) {
    return new Promise((resolve, reject) => {
      let count = 0, result = [], len = iterable.length
      if (len === 0) {
          resolve(result)
      }
      
      function resolver (i) {
          return function (x) {
              result[i] = x
              count++
              if (count === i) {
                  resolve(i)
              }
          }
      }
      for(let i = 0; i < len; i++) {
          Promise.resolve(iterable[i]).then(resolver(i), reject)
      }
    })
}

复制代码

6. race

/**
**** 谁的异步时间短,谁先处理,其余忽略
***/
static race(iterable) {
    return new Promise((resolve, reject) => {
        for (let i = 0, len = iterable.length ; i < len; i++ ) {
            Promise.resolve(iterable[i]).then(resolve, reject)
        }
    })
}

复制代码

二. 核心

为了避免不必要的干扰,省略代码将以 @code_{function_name}_{number}标号。部分优化、兼容代码将省略,例如对setTimeout的优化,将直接使用setTimeout,并不会影响代码逻辑

1. constructor

/**
*  @constructor 初始化代码
*  1. 初始化代码进行常规的类型检查和属性赋值
*  2. @_state状态
*       penging   <===> 0
*       fulfilled <===> 1
*       rejected  <===> 2
*       <Promise> <===> 3 (自定义的state,用于取<Promise>内部的值,在下文详解)
*  3.@_handled标记当前Promise实例的结果是否被处理过,初始化为false
*  3.@_deferreds 存放在then中的注册的函数,元素形式{onFulfilled, onRejected,promise}
*  4.@doResolve 详见下文
**/
constructor (executor) {
  if (!(this instanceof Promise))
    throw new TypeError('Promises must be constructed via new')
  if (typeof fn !== 'function') throw new TypeError('not a function')
  /** @type {!number} */
  this._state = 0
  /** @type {!boolean} */
  this._handled = false
  /** @type {Promise|undefined} */
  this._value = undefined
  /** @type {!Array<!Function>} */
  this._deferreds = []

  doResolve(fn, this)
}

/***
** @doResolve分发self给resolve和reject进行状态转换
** @done确保当前实例的状态由penging=>fulfilled 或者 penging=>rejected只发生一次
** @resolve、 reject 全局方法,处理状态转换
**/

function doResolve (fn, self) {
  var done = false
  try {
    fn(
      function (value) {
        if (done) return
        done = true
        resolve(self, value)
      },
      function (reason) {
        if (done) return
        done = true
        reject(self, reason)
      }
    )
  } catch (ex) {
    if (done) return
    done = true
    reject(self, ex)
  }
}

复制代码

2. then

executor执行完同步代码,紧接着将在then中注册方法onFulfilled、onRejected挂载到一个deferred对象上

// 空函数
const noop = function() {}

/**
**  1. 先创建一个Promise实例prom
**  2. new Handler(onFulfilled, onRejected, prom) 创建一个deferred对象挂载then上注册的方法和下个promise实例prom <deferred: {onFulfilled, onRejected, promise}>
**  3. handle在此将 deferred加入到当前promise实例的 _deferreds队列上
**  4. 返回Promise的实例prom,供后面的then、catch、继续注册方法
**  5. 返回一个新Promise的实例是为了确保pending => fulfilled 或者 pending => rejected
***/
then(onFulfilled, onRejected) {
    let prom = new this.constructor(noop)
    handle(this, new Handler(onFulfilled, onRejected, prom))
    return prom
}

/**
*** @Handler 创建一个deferred <deferred: {onFulfilled, onRejected, promise}>
**  onFulfilled, onRejected不是函数则赋值为null,方便在handle中统一处理
**/
function Handler (onFulfilled, onRejected, promise) {
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
  this.onRejected = typeof onRejected === 'function' ? onRejected : null
  this.promise = promise
}

/***
*** then方法中调用handle就只是为了将deferred添加到当前Promise实例的_deferreds上,等待最后处理Promise的返回值
***/
function handle (self, deferred) {
    // ...@code_handle_1
    if (self._state === 0) {
        self._deferreds.push(deferred)
        return
    }
    // ...@code_handle_2
}
复制代码

3. Promise结果处理

我们先将假设Promise的状态已发生改变,如何改变的先不表。promise-polyfill把结果统一在finale中处理

/***
****  @function finale
***   1. 当前实例在rejected状态且未注册deferred,注册一个定时器在下个事件循环中检查Promise的值是否被调用过
***   2. 循环调用调用handle,用当前实例的_deferreds队列中注册的方法处理promise的返回值
***   3. 释放当前实例的_deferreds的空间
***/
function finale (self) {
  if (self._state === 2 && self._deferreds.length === 0) {
    setTimeout(function () { // 原代码有优化,此处代码改动,但逻辑未变
      if (!self._handled) {
        if (typeof console !== 'undefined' && console) {
          console.warn('Possible Unhandled Promise Rejection:', self.__value)// eslint-disable-line no-console
        }
      }
    })
  }
  
  for (var i = 0, len = self._deferreds.length; i < len; i++) {
    handle(self, self._deferreds[i])
  }
  self._deferreds = null
}


/**
** @function handle
** 1. while循环检查当前实例的值是否是一个Promise实例,self = self._value直到self的值不是promise
** 2. self._state === 0,主要用在then方法中使用在添加deferred对象
** 3. 标识当前promise实例已调用
** 4. 启动一个定时器,注册方法在下个循环中处理当前promise结果
**/

function handle (self, deferred) {
  while (self._state === 3) {
    self = self._value
  }
  if (self._state === 0) {
    self._deferreds.push(deferred)
    return
  }
  self._handled = true
  setTimeout(function () {
  // 根据当前promise状态,取挂载在deferred对象上的对应方法(在then方法中注册的方法)
    var cb = self._state === 1 ? deferredeferred.onFulfilled : deferred.onRejected
    // 如果cb==null,则表示未在then中注册方法,直接根据当前promise的状态,调用resolve或reject,将当前值放到下一个then注册的方法中,退出当前调用栈
    if (cb === null) {
      (self._state === 1 ? resolve : reject)(deferred.promise, self._value)
      return
    }
    var ret
    try {
    // 由then中注册的方法调用当前promise的值,返回值放入ret
      ret = cb(self._value)
    } catch (e) {
    // 如果有异常将异常结果作为当前实例then注册方法的值传入下个promise供then注册的onRejected方法调用
      reject(deferred.promise, e)
      return
    }
    // 将ret作为当前promise实例then方法中的值传入下个promise的then注册的onFulfilled方法调用
    resolve(deferred.promise, ret)
  })
}

复制代码

4.全局resolve、reject函数

非Promise实例方法,用作处理Promise的状态转移

/**
******@function resolve
** 处理 penging ===> fulfilled/rejected的状态转移
**  1. 检查Promise实例是否resolve调用自身,报错 转rejected
**  2. 检查newValue返回值是否是一个Promse实例,设置私有状态3(最后将在handle中解析成非promise值),传递给finale进一步处理
**  3. 检查newValue是否是一个thenable类型,调用doResolve
**  4. 非Promise的值,state设为1,传递给finale进一步处理
***/
function resolve (self, newValue) {
  try {
    // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
    // 对应规范2.3.1: 如果promise(self)和x(newValue)引用同一个对象,请以promisea TypeError为理由拒绝。
    if (newValue === self) { throw new TypeError('A promise cannot be resolved with itself.') }
    if (
      newValue &&
      (typeof newValue === 'object' || typeof newValue === 'function')
    ) {
      var then = newValue.then
      if (newValue instanceof Promise) {
        self._state = 3
        self._value = newValue
        finale(self)
        return
      } else if (typeof then === 'function') {
      /**
      **  对应规范 2.3.3.3 如果then是函数,请使用x as this,first参数resolvePromise和第二个参数调用它rejectPromise
      **/
        doResolve(bind(then, newValue), self)
        return
      }
    }
    self._state = 1
    self._value = newValue
    finale(self)
  } catch (e) {
    reject(self, e)
  }
}

/**
**  Polyfill for Function.prototype.bind
**/
function bind (fn, thisArg) {
  return function () {
    fn.apply(thisArg, arguments)
  }
}

/***
*   修改状态,传递给finale进一步处理
***/
function reject (self, newValue) {
  self._state = 2
  self._value = newValue
  finale(self)
}

复制代码

结尾

总算水完了,看着好像也没啥干货,错漏之处恳求指点。

猜你喜欢

转载自juejin.im/post/5c838860f265da2ddd4a8ff5