Promise 源码分析

栏目: JavaScript · 发布时间: 7年前

内容简介:从index.js当中知道,它是先引出了我们首先先想一下最基础的promise用法

前言

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 ★,转载请注明出处。

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


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Head First Rails

Head First Rails

David Griffiths / O'Reilly Media / 2008-12-30 / USD 49.99

Figure its about time that you hop on the Ruby on Rails bandwagon? You've heard that it'll increase your productivity exponentially, and allow you to created full fledged web applications with minimal......一起来看看 《Head First Rails》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具