Promise 流水账

81 阅读4分钟

源码开头

//
// 0 - pending
// 1 - fulfilled with _value
// 2 - rejected with _value
// 3 - adopted the state of another promise, _value
//
// once the state is no longer pending (0) it is immutable

// All `_` prefixed properties will be reduced to `_{random number}`
// at build time to obfuscate them and discourage their use.
// We don't use symbols or Object.defineProperty to fully hide them
// because the performance isn't good enough.
  • 有 0-3 四种状态,分别对应,创建初始化、成功响应、异常响应、响应了一个Promise
  • 选择在私有属性上使用 ‘_’ + 随机数作为前缀,这样比其他方式性能好

准备工作


var asap = require('asap');

function noop() {}

// to avoid using try/catch inside critical functions, we
// extract them to here.
var LAST_ERROR = null;
var IS_ERROR = {};
function getThen(obj) {
  try {
    return obj.then;
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

function tryCallOne(fn, a) {
  try {
    return fn(a);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}
function tryCallTwo(fn, a, b) {
  try {
    fn(a, b);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}
  • 引入了异步包asap
  • 函数式的noop
  • 用于触发 Then 监听方法的 tryCallOne
  • 用于捕获 resolve、reject 的 tryCallTwo
  • 捕获获取then方法可能的异常,在判断响应值是否是promise时使用,应对不同人写的符合特征的promise

使用new Promise(execute(resolve, reject))时

function Promise(fn) {
  if (typeof this !== 'object') {
    throw new TypeError('Promises must be constructed via new');
  }
  if (typeof fn !== 'function') {
    throw new TypeError('Promise constructor\'s argument is not a function');
  }
  this._U = 0;
  this._V = 0;
  this._W = null;
  this._X = null;
  if (fn === noop) return;
  doResolve(fn, this);
}
  • 参数校验
  • V是状态,对应源码开头提到的4种
  • U是监听/等待的状态,0是没有,1是一个,2是超过一个
  • W是返回值
  • X是监听者,按照U的情况,值的范围:单个回调方法 / 回调方法集合数组
  • noop完成了职责

doResolve

/**
 * Take a potentially misbehaving resolver function and make sure
 * onFulfilled and onRejected are only called once.
 *
 * Makes no guarantees about asynchrony.
 */
function doResolve(fn, promise) {
  var done = false;
  var res = tryCallTwo(fn, function (value) {
    if (done) return;
    done = true;
    resolve(promise, value);
  }, function (reason) {
    if (done) return;
    done = true;
    reject(promise, reason);
  });
  if (!done && res === IS_ERROR) {
    done = true;
    reject(promise, LAST_ERROR);
  }
}
  • 调用传入的方法 execute, 并且传入 resolve, reject
  • 在方法异常或者方法主动调用reject外的情况调用resolve
  • 用done变量防止重复调用

resolve

function resolve(self, newValue) {
  // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  if (newValue === self) {
    return reject(
      self,
      new TypeError('A promise cannot be resolved with itself.')
    );
  }
  if (
    newValue &&
    (typeof newValue === 'object' || typeof newValue === 'function')
  ) {
    var then = getThen(newValue);
    if (then === IS_ERROR) {
      return reject(self, LAST_ERROR);
    }
    if (
      then === self.then &&
      newValue instanceof Promise
    ) {
      self._V = 3;
      self._W = newValue;
      finale(self);
      return;
    } else if (typeof then === 'function') {
      doResolve(then.bind(newValue), self);
      return;
    }
  }
  self._V = 1;
  self._W = newValue;
  finale(self);
}
  • 先防止响应的数据是自身这个Promise, 防止死循环
  • 然后对于响应值是Promise的情况进行兼容
    1. 如果是不合规的Promise,响应异常,触发reject
    2. 如果是自身同源Promise,状态变为3,填充响应值
    3. 如果是他人写的Promise,触发他的Then方法,填充响应值
  • 如果以上都不是,修改自身状态为已完成,填充响应值,触发finale

reject

function reject(self, newValue) {
  self._V = 2;
  self._W = newValue;
  if (Promise._Z) {
    Promise._Z(self, newValue);
  }
  finale(self);
}
  • 忽略Promise._Z,这是一个联动逻辑
  • 修改自身状态为已失败,填充异常值,触发finale

finale

function finale(self) {
  if (self._U === 1) {
    handle(self, self._X);
    self._X = null;
  }
  if (self._U === 2) {
    for (var i = 0; i < self._X.length; i++) {
      handle(self, self._X[i]);
    }
    self._X = null;
  }
}
  • 判断一下接受响应的数目状态,最后都是交与handle处理

调用 promise.then() 时

Promise.prototype.then = function(onFulfilled, onRejected) {
  if (this.constructor !== Promise) {
    return safeThen(this, onFulfilled, onRejected);
  }
  var res = new Promise(noop);
  handle(this, new Handler(onFulfilled, onRejected, res));
  return res;
};

function safeThen(self, onFulfilled, onRejected) {
  return new self.constructor(function (resolve, reject) {
    var res = new Promise(noop);
    res.then(resolve, reject);
    handle(self, new Handler(onFulfilled, onRejected, res));
  });
}

function Handler(onFulfilled, onRejected, promise){
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  this.promise = promise;
}
  • var res = new Promise(noop); 创造了一个不会变化的Promise
  • 然后就简单返回noop的promise
  • 如果then方法触发时不在自身,就用自身包裹一下再返回,目的是延后这个操作,等其他人写的Promise先执行

handle

function handle(self, deferred) {
  while (self._V === 3) {
    self = self._W;
  }
  if (Promise._Y) {
    Promise._Y(self);
  }
  if (self._V === 0) {
    if (self._U === 0) {
      self._U = 1;
      self._X = deferred;
      return;
    }
    if (self._U === 1) {
      self._U = 2;
      self._X = [self._X, deferred];
      return;
    }
    self._X.push(deferred);
    return;
  }
  handleResolved(self, deferred);
}
  • 如果响应值是Promise,就用循环拆包
  • 忽略 Promise._Y,联动处理
  • 如果then时,还没有结果可以响应,就填入等待 对象/数组
  • 这里在多次调用中调整 U 的值,deferred存储等待触发的接收方法
  • 如果已经 完成/失败 直接触发 handleResolved

handleResolved

function handleResolved(self, deferred) {
  asap(function() {
    var cb = self._V === 1 ? deferred.onFulfilled : deferred.onRejected;
    if (cb === null) {
      if (self._V === 1) {
        resolve(deferred.promise, self._W);
      } else {
        reject(deferred.promise, self._W);
      }
      return;
    }
    var ret = tryCallOne(cb, self._W);
    if (ret === IS_ERROR) {
      reject(deferred.promise, LAST_ERROR);
    } else {
      resolve(deferred.promise, ret);
    }
  });
}
  • 用 asap 统一化了异步调用,意味着用Promise包装同步方法,也会异步执行
  • 用 asap 有一个好处,监听者很多时,不会造成执行的占用,每一个监听都是一次处理
  • 判断自身状态去触发 then/catch 方法
  • 触发之前 noop版promise的响应,也就是触发链式的Then/catch

收尾

因为有递归在里面,初看时没注意差点被绕进去,不过本身还是比想象简单。