Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2018/12/21 - Promise 源码分析 #29

Open
wusb opened this issue Dec 21, 2018 · 0 comments
Open

2018/12/21 - Promise 源码分析 #29

wusb opened this issue Dec 21, 2018 · 0 comments

Comments

@wusb
Copy link
Contributor

wusb commented Dec 21, 2018

前言

then/promise项目是基于Promises/A+标准实现的Promise库,从这个项目当中,我们来看Promise的原理是什么,它是如何做到的,从而更加熟悉Promise

分析

从index.js当中知道,它是先引出了./core.js,随后各自执行了其他文件的代码,通过requeire的方法。

我们首先先想一下最基础的promise用法

new Promise((resolve, reject) =>  {
    resolve(4);

}).then(res => {
    console.log(res); // export 4
});

Promise中的标准

标准中规定:

  1. Promise对象初始状态为 Pending,在被 resolvereject 时,状态变为 FulfilledRejected
  2. resolve接收成功的数据,reject接收失败或错误的数据
  3. Promise对象必须有一个 then 方法,且只接受两个可函数参数 onFulfilledonRejected

index.js

'use strict';

module.exports = require('./core.js');
require('./done.js');
require('./finally.js');
require('./es6-extensions.js');
require('./node-extensions.js');
require('./synchronous.js');

我们先看src/core.js

function Promise(fn) {
  // 判断 this一定得是object不然就会报错,这个方法一定得要new出来
  if (typeof this !== 'object') {
    throw new TypeError('Promises must be constructed via new');
  }
  // 判断fn 一定得是一个函数
  if (typeof fn !== 'function') {
    throw new TypeError('Promise constructor\'s argument is not a function');
  }
  this._deferredState = 0;
  this._state = 0;
  this._value = null;
  this._deferreds = null;
  if (fn === noop) return;
  // 最终doResolve很关键
  doResolve(fn, this);
}

Promise是一个构造方法,开始时,它进行了校验,确保了fn是一个函数,随后对一些变量进行了初始化,最后执行了doResolve()

我们接着看doResolve这个方法。

/**
 * Take a potentially misbehaving resolver function and make sure
 * onFulfilled and onRejected are only called once.
 *
 * Makes no guarantees about asynchrony.
 */
// 
// 确保`onFulfilled`和`onRejected`方法只调用一次
// 不保证异步
function doResolve(fn, promise) {
  var done = false;
  var res = tryCallTwo(fn, function (value) {
    // 如果done 为true 则return
    if (done) return;
    done = true;
    // 回调执行 resolve()
    resolve(promise, value);
  }, function (reason) {
    // 如果done 为true 则return
    if (done) return;
    done = true;
    reject(promise, reason);
  });
  // res为truCallTwo()的返回值
  // 如果done没有完成 并且 res 是 `IS_ERROR`的情况下
  // 也会执行reject(),同时让done完成
  if (!done && res === IS_ERROR) {
    done = true;
    reject(promise, LAST_ERROR);
  }
}

doResolve最关键的是执行了tryCallTwo方法,这个方法的第二,第三个参数都是回调,当执行回调后,done为true,同时各自会执行resolve()或者reject()方法。最后当tryCallTwo的返回值为IS_ERROR时,也会执行reject()方法。

我们先来看一下tryCallTwo方法

function tryCallTwo(fn, a, b) {
  try {
    fn(a, b);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

fn实际就是Promise初始化时的匿名函数(resolve, reject) => {}ab则代表的是resolve()reject()方法,当我们正常执行完promise函数时,则执行的是resolve则在doResolve中,我们当时执行的第二个参数被回调,如果报错,reject()被执行,则第二个参数被回调。最后捕获了异常,当发生了报错时,会return IS_ERROR,非报错时会return undinfed

再回到刚才的doResolve方法,当执行了第二个参数的回调之后,会执行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.')
    );
  }
  // promise作为参数
  if (
    newValue &&
    (typeof newValue === 'object' || typeof newValue === 'function')
  ) {
    // 获取它的promise方法 读取newValue.then
    var then = getThen(newValue);
    if (then === IS_ERROR) {
      // 如果then IS_ERROR
      return reject(self, LAST_ERROR);
    }
    if (
      // 如果then是self的then
      // 并且Promise
      then === self.then &&
      // newValue 属于Promise
      newValue instanceof Promise
    ) {
      // _state为3
      // 一般then之后走这里
      // 执行then(newValue)返回了promise
      self._state = 3;
      // selft.value为newValue
      self._value = newValue;
      // 当state为3时执行 finale
      finale(self);
      return;
    } else if (typeof then === 'function') {
      doResolve(then.bind(newValue), self);
      return;
    }
  }
  self._state = 1;
  self._value = newValue;
  finale(self);
}

在没有链式调用then的情况下(也就是只要一个then)的情况下,会将内部状态_state设置成3,将传入值赋给内部变量_value最后会执行final()方法,不然则会使用doResolve来调用then

我们再来看下reject

function reject(self, newValue) {
  // _state = 2为reject
  self._state = 2;
  self._value = newValue;
  if (Promise._onReject) {
    Promise._onReject(self, newValue);
  }
  finale(self);
}

reject当中我们的_state变更为了2,同样最后finale被调用。

我们来看下finale函数

// 执行自己的deferreds
function finale(self) {
  if (self._deferredState === 1) {
    handle(self, self._deferreds);
    self._deferreds = null;
  }
  if (self._deferredState === 2) {
    for (var i = 0; i < self._deferreds.length; i++) {
      // 遍历handle
      handle(self, self._deferreds[i]);
    }
    // 将deferred 置空
    self._deferreds = null;
  }
}

在该方法当中根据不同的_deferredState,会执行不同的handle方法。

我们再来看handle方法

function handle(self, deferred) {
  while (self._state === 3) {
    self = self._value;
  }
  // 如果有onHandle方法 则执行该方法
  if (Promise._onHandle) {
    Promise._onHandle(self);
  }
  // (初始 _state 为0)
  if (self._state === 0) {
    // (初始 _deferredState 为0)
    if (self._deferredState === 0) {
      self._deferredState = 1;
      self._deferreds = deferred;
      return;
    }
    // 如果 _deferredState是1 则__deferreds是一个数组
    if (self._deferredState === 1) {
      self._deferredState = 2;
      self._deferreds = [self._deferreds, deferred];
      return;
    }
    // 当走到这里 _deferredState应该是2 将deferred
    // 插入到数组当中
    self._deferreds.push(deferred);
    return;
  }
  handleResolved(self, deferred);
}

这里比较关键的应该就是通过deferredState不同的状态,将deferred放入deferreds当中。另外当我们的_state不为0时,最终会执行handleResolved

继续看handleResolve()方法

function handleResolved(self, deferred) {
  asap(function() {
    // _state为1时,cb = onFulfilled 否则 cb = onRejected
    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
    if (cb === null) {
      if (self._state === 1) {
        resolve(deferred.promise, self._value);
      } else {
        reject(deferred.promise, self._value);
      }
      return;
    }
    var ret = tryCallOne(cb, self._value);
    if (ret === IS_ERROR) {
      reject(deferred.promise, LAST_ERROR);
    } else {
      resolve(deferred.promise, ret);
    }
  });
}.then((res) => {
}).catch((error) => {
})

在这个方法当中,会根据我们任务(_state)的不同状态,来执行onFulfilled或者onRejected方法。当此方法调用时,也就是我们一个简单的Promise的结束。

回到刚才说的Promise构造方法结束的时候

设置了Promise函数的一些变量

Promise._onHandle = null;
Promise._onReject = null;
Promise._noop = noop;

随后在Promise的原型上设置了then方法。

Promise.prototype.then = function(onFulfilled, onRejected) {
  // 首先看这是谁构造的 如果不是promise
  // 则return 执行safeThen
  if (this.constructor !== Promise) {
    return safeThen(this, onFulfilled, onRejected);
  }
  // 如果是则初始化一个Promise 但是参数 noop 为空对象 {}
  var res = new Promise(noop);
  // 随后执行handle方法
  handle(this, new Handler(onFulfilled, onRejected, res));
  return res;
};

then这个方法中首先判断了它是否由Promise构造的,如果不是,则返回并执行safeThen,不然则执行Promise构造一个res对象,然后执行handle方法,最后将promise变量res返回。handle方法之前有提过,在这里,当初始化时_state_deferred的转改都为0,因此它会将defrred保存到promise当中。

先看一下上面说的safeThen方法

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));
  });
}

流程

需要有一个Promise的构造方法,这个构造方法最终会执行它的参数(resolve, reject) => {},声明的then方法会通过handle()方法将onFulfilledonRejected方法保存起来。当在外部调用resolve或者onRejected时,最终也会执行handle但是它,会最后根据状态来执行onFulfilled或者onRejected。从而到我们的then回调中。

Promise的扩展

done

done的扩展在src/done.js当中

'use strict';

var Promise = require('./core.js');

module.exports = Promise;
Promise.prototype.done = function (onFulfilled, onRejected) {
  var self = arguments.length ? this.then.apply(this, arguments) : this;
  self.then(null, function (err) {
    setTimeout(function () {
      throw err;
    }, 0);
  });
};

内部执行了then()

finally

finally的扩展在src/finally.js当中

Promise的标准当中,本身是没有finally方法的,但是在ES2018的标准里有,finally的实现如下

'use strict';

var Promise = require('./core.js');

module.exports = Promise;
Promise.prototype.finally = function (callback) {
  return this.then(function (value) {
    return Promise.resolve(callback()).then(function () {
      return value;
    });
  }, function (err) {
    return Promise.resolve(callback()).then(function () {
      throw err;
    });
  });
};

PromiseonFulfilledonRejected 不管回调的哪个,最终都会触发callback 回调。还要注意的一点是finally的返回也是一个Promise

es6-extensions.js

es6-extensions.js文件当中包含了ES6的一些扩展。

Promise.resolve

function valuePromise(value) {
  var p = new Promise(Promise._noop);
  // 将_state赋值为 非0
  // _value进行保存
  p._state = 1;
  p._value = value;
  // 这样做的目的是省略的一些前面的逻辑
  return p;
}

Promise.resolve = function (value) {
  if (value instanceof Promise) return value;

  if (value === null) return NULL;
  if (value === undefined) return UNDEFINED;
  if (value === true) return TRUE;
  if (value === false) return FALSE;
  if (value === 0) return ZERO;
  if (value === '') return EMPTYSTRING;

  // value return new Promise
  if (typeof value === 'object' || typeof value === 'function') {
    try {
      var then = value.then;
      if (typeof then === 'function') {
        // 返回 返回了一个新的Promise对象
        return new Promise(then.bind(value));
      }
    } catch (ex) {
        // 如果报错 则返回一个就只
      return new Promise(function (resolve, reject) {
        reject(ex);
      });
    }
  }

  return valuePromise(value);
};

Promise.reject

Promise.reject = function (value) {
  return new Promise(function (resolve, reject) {
    reject(value);
  });
};

Promise.all

Promise.all = function (arr) {
  // 类似深拷贝了一份给了args
  var args = Array.prototype.slice.call(arr);

  return new Promise(function (resolve, reject) {
    // 判断了all的promise数量
    if (args.length === 0) return resolve([]);
    // remaining则是promise数组的长度
    var remaining = args.length;
    // i为index val 为 promise
    function res(i, val) {
      if (val && (typeof val === 'object' || typeof val === 'function')) {
        if (val instanceof Promise && val.then === Promise.prototype.then) {
          while (val._state === 3) {
            val = val._value;
          }
          if (val._state === 1) return res(i, val._value);
          if (val._state === 2) reject(val._value);
          // val._state 为 0时 走这里
          val.then(function (val) {
            res(i, val);
          }, reject);
          return;
        } else {
          var then = val.then;
          if (typeof then === 'function') {
            var p = new Promise(then.bind(val));
            p.then(function (val) {
              res(i, val);
            }, reject);
            return;
          }
        }
      }
      args[i] = val;
      // 当所有的promise执行完 则是remaining为0
      // 则执行resolve();
      if (--remaining === 0) {
        resolve(args);
      }
    }
    // 遍历所有的promise
    for (var i = 0; i < args.length; i++) {
      res(i, args[i]);
    }
  });
};

Promise.all()返回的也是一个Promise函数。
内部有一个remaining变量每当执行完一个promise函数后就会减一,当所有promise执行完,会执行自己的resolve

Promise.race

Promise.race = function (values) {
  return new Promise(function (resolve, reject) {
    values.forEach(function(value){
      Promise.resolve(value).then(resolve, reject);
    });
  });
};

遍历传入的promise数组,经过Promise.resolve(value)的源码可以看到,如果value是一个Promise则户直接将这个value返回,最后数组中的promise哪个优先回调即执行。

Promise.property.catch

catch在标准当中也是没有,虽然我们用的比较多

Promise.prototype['catch'] = function (onRejected) {
  return this.then(null, onRejected);
};

catch的回调实际是then(null, onRejected)的回调。

广而告之

本文发布于薄荷前端周刊,欢迎Watch & Star ★,转载请注明出处。

欢迎讨论,点个赞再走吧 。◕‿◕。 ~

@wusb wusb changed the title 2018/12/21 - promise 源码分析 2018/12/21 - Promise 源码分析 Dec 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant