Promise源码解析

Promise简介

Promise 是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大。现已被 ES6 归入进规范中。git

使用方式

new Promise((resolve, reject) => {
    ajax({
        success(res) {
            resolve(res);
        },
        error(err) {
            reject(err);
        }
    })
}).then((res) => {
    // 请求正常处理
})
    .catch((err) => {
        // 错误处理
    })
复制代码

基本源码分析

成功回调处理

首先咱们须要一个promise的使用实例代码,以下:github

示例代码1

new Promise((resolve) => {
    setTimeout(() => {
        resolve(123);
    }, 0);
}).then((res) => {
    console.log(res);
})
复制代码

事例代码中,首先new了一个Promise对象,源码中Promise对象的构造函数长这样:ajax

Promise构造函数

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._deferredState = 0; // then的状态
    this._state = 0; // 状态,初始状态是0表示pending,1表示fulfilled,2表示rejected
    this._value = null; // 获取到的值
    this._deferreds = null; //  存储的成功回调和失败回调,以及返回的新Promis的handler
    if (fn === noop) return;
    doResolve(fn, this);
}
复制代码

先检测是否用new的方式实例化promise对象,再检查传入的方法是否为方法,若检查都经过,就初始化一些参数,以后将传入promise的方法fn和promise实例做为参数传给doResolve方法。doResolve方法以下:编程

doResolve方法

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);
    }
}
复制代码

tryCallTwo方法接受3个参数,第一个是传给Promise的异步方法fn,真实调用的resolve方法,以及真实调用的reject方法。tryCallTwo方法以下:数组

tryCallTwo

function tryCallTwo(fn, a, b) {
  try {
    fn(a, b); // 执行传入promise的方法,而且将resolve和reject方法做为参数传入
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}
复制代码

异步操做还未返回

由于咱们的示例只是一个setTimeout的一个回调,因此不会走到报错的环节,setTimeout会放入任务队列中,延迟执行resolve。接下来会先执行then方法,Promise中的then方法挂在prototype上面: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;
};
复制代码
  • 若是不是在Promise实例上调用then会调用safeThen(这一段我也不是很理解,会有不在Promise实例调用then的状况嘛?但愿有大神能解释)。
  • 如果正常Promise实例调用then,则新建一个Promise实例(then中返回的实例,用于链式调用)
  • 把正确的回调和报错的回调以及返回的Promise实例都做为参数,传入Handler的构造方法中。Handler构造方法以下:

Handler构造函数

function Handler(onFulfilled, onRejected, promise){
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  this.promise = promise;
}
复制代码

Handler的构造方法仅仅只是将成功回调和失败回调,以及返回的下一个Promise实例做为私有变量保存下来。接着将Handler实例做为参数传入handle方法中:bash

handle(this, new Handler(onFulfilled, onRejected, res));
复制代码

handle方法代码以下:异步

handle方法

function handle(self, deferred) {
  while (self._state === 3) { // 处理resolve的值是个promise实例的状况
    self = self._value;
  }
  if (Promise._onHandle) { // 若定义了_onHandle方法,则调用
    Promise._onHandle(self);
  }
  if (self._state === 0) { // 异步请求还没返回
    if (self._deferredState === 0) { // then方法以前还未调用
      self._deferredState = 1; // 进入改逻辑说明then已经调用,改成状态为2
      self._deferreds = deferred; // handler
      return;
    }
    if (self._deferredState === 1) { // Promise实例又执行了一遍then
      self._deferredState = 2; // 在请求未返回状况下,第二次调用then,状态为2
      self._deferreds = [self._deferreds, deferred]; // 在请求未返回状况下,第二次调用then,deferreds改为数组,存储多个handler
      return;
    }
    self._deferreds.push(deferred); // 在请求未返回状况下,屡次调用then,将handler依次放入队列
    return;
  }
  handleResolved(self, deferred);
}
复制代码

此时咱们的逻辑走到这里,把then状态修改成1,存储了handler后返回:异步编程

if (self._deferredState === 0) {
      self._deferredState = 1;
      self._deferreds = deferred;
      return;
}
复制代码

异步请求返回啦

回顾doResolve方法:函数

function doResolve(fn, promise) {
    var done = false;
    var res = tryCallTwo(fn, function (value) {
        if (done) return;
        done = true;
        resolve(promise, value); // 实例代码中resolve(123),因此逻辑走到这里
    }, function (reason) {
        if (done) return;
        done = true;
        reject(promise, reason);
    });
    if (!done && res === IS_ERROR) {
        done = true;
        reject(promise, LAST_ERROR);
    }
}
复制代码

resolve方法以下:

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._state = 3;
      self._value = newValue;
      finale(self);
      return;
    } else if (typeof then === 'function') {
      doResolve(then.bind(newValue), self);
      return;
    }
  }
  self._state = 1;
  self._value = newValue;
  finale(self);
}
复制代码

此时咱们只是resolve了一个123,既不是对象也不是Promise,因而只走这段逻辑:

self._state = 1; // state为1表示fulfilled
self._value = newValue; // 123
finale(self);
复制代码

执行finale方法:

finale

function finale(self) {
  if (self._deferredState === 1) { // then已经执行
    handle(self, self._deferreds); // 走到这里
    self._deferreds = null;
  }
  if (self._deferredState === 2) { // 多从调用then,则通知全部then注册的回调所有执行
    for (var i = 0; i < self._deferreds.length; i++) {
      handle(self, self._deferreds[i]);
    }
    self._deferreds = null;
  }
}
复制代码

再次执行handle方法,此时state和defferedSate值都为1,则执行handleResolved方法

handleResolved(self, deferred);
复制代码

handleResolved

function handleResolved(self, deferred) {
  asap(function() {
    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);
    }
  });
}
复制代码
  • asap是一个第三方库,功能相似与setImmediate
  • 判断当前state的状态,决定要执行的回调是成功回调仍是失败回调
  • 若没设置过回调,则把value传给下一个Promise
  • 执行tryCallOne

tryCallOne

function tryCallOne(fn, a) {
  try {
    return fn(a);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}
复制代码

在tryCallOne中执行对应回调,若没有报错的话,将返回值做为value传递给下一个Promise。咱们的示例代码中的console.log(res)就会执行,打印出123。

一次不报错的Promise调用流程

咱们整理一下,若是调用一次Promise,且没发生报错的状况下,发生了什么事情

错误回调处理

若Promise中传入的方法,发生错误,这时候会发生什么?

示例代码2

new Promise((resolve) => {
    resolve(a); // a并无定义,发生报错
}).then((res) => {
    console.log(res);
})
复制代码

回到源码中到doResolve方法以及内部的tryCallTwo方法

// doResolve
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);
  }
}
// tryCallTwo
function tryCallTwo(fn, a, b) {
  try {
    fn(a, b);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}
复制代码

tryCallTwo方法里使用了try-catch,若发生报错LAST_ERROR会保存此次报错的信息,并返回一个错误。以后promise会自动执行reject。

reject

function reject(self, newValue) {
  self._state = 2;
  self._value = newValue;
  if (Promise._onReject) {
    Promise._onReject(self, newValue);
  }
  finale(self);
}
复制代码

reject跟resolve方法相似,只是把state状态变成了2而已,value保存为此次报错的信息。而后then方法中调用handleResolved时候,选择执行第二个传入的方法。

var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
复制代码

但咱们大多数开发的时候,then都没有传入第二个方法,而是使用catch来捕获错误。

new Promise((resolve) => {
    resolve(a); // a并无定义,发生报错
}).then((res) => {
    console.log(res);
})
    .catch((err) => {
        // ...
    })
复制代码

catch的实现很是简单,只是调用了一下then方法,可是不传入正确回调的方法。

catch

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

不过有一种状况,在异步操做返回以后的报错,try-catch没法捕获到,这时候须要在返回的方法体中手动reject报错信息。

new Promise((resolve, reject) => {
    setTimeout(() => {
        try {
            resolve(a); // a并无定义,发生报错
        } catch(err) {
            reject(err); // 由于异步的报错,须要手动reject
        }
    }, 0);
}).then((res) => {
    console.log(res);
})
    .catch((err) => {
        // ...
    })
复制代码

Promise正常使用的流程图

resolve一个Promise实例

实例代码3

new Promise((resolve) => {
    resolve(new Promise((resolve) => {
        resolve(111);
    }));
}).then((res) => {
    console.log(111);
});
复制代码

resolve方法里面,若是resolve的是一个Promise会走入如下逻辑:

if (
      then === self.then &&
      newValue instanceof Promise
    ) {
      self._state = 3;
      self._value = newValue;
      finale(self);
      return;
    }
复制代码

状态值设置为3,而且把resolve内部的Promise实例赋值给value。 handle方法中的处理

while (self._state === 3) { // state状态为3的时候,把resolve内的promise设置为当前promise
    self = self._value;
  }
}
复制代码

因而Promise内resolve一个Promise的时候,接下来调用的then方法中回调中的参数是resolve内部的Promise的value。代码3中打印出来的值应该是111。

then中return一个Promise

链式调用then的时候,then中的回调执行的值,将做为返回的Promise的value,也就是下一次then时候传给回调的值。

new Promise((resolve) => {
    resolve(1);
}).then((resolve) => {
    resolve(2);
}).then((res) => {
    console.log(res); // 2
})
复制代码

在Promise代码执行完毕后,handleResolved方法后面还有一个这样的逻辑

var ret = tryCallOne(cb, self._value);
if (ret === IS_ERROR) {
  reject(deferred.promise, LAST_ERROR);
} else {
  resolve(deferred.promise, ret);
}
复制代码

若是回调方法没有报错,回调函数return的值将用resolve的方式做为value传给then中返回的Promise。那若是return一个Promise将会和resolve(new Promise)同样,return的Promise将取代原先then中返回的Promise。下一个then中传递的参数也将是手动return的Promise的value。如:

new Promise((resolve) => {
    resolve(1);
}).then((res) => {
    return new Promise((resolve) => {
        resolve(2);
    });
}).then((res) => {
    console.log(res) // 2
})
复制代码

Promise.all

有时候须要多个请求并行发送,就要使用到Promise.all

Promise.all源码

Promise.all = function (arr) {
  var args = Array.prototype.slice.call(arr);
  return new Promise(function (resolve, reject) {
    if (args.length === 0) return resolve([]);
    var remaining = args.length;
    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.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;
      if (--remaining === 0) {
        resolve(args);
      }
    }
    for (var i = 0; i < args.length; i++) {
      res(i, args[i]);
    }
  });
};
复制代码

能够看出Promise.all也是返回了一个Promise,这个Promise内部resolve了一个返回全部结果的数组。Promise.all会接受一个数组,以后遍历这个数组,数组的每一个成员都会执行一遍res这个方法

res

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.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;
      if (--remaining === 0) {
        resolve(args);
      }
    }
复制代码

判断传入Promise实例的状态:

若是state为3(里面resolve了Promise)当前的Promise更换为value。

state为2说明,队列中的该Promise内部状态为reject,则直接在Promise.all执行reject。因此为何Promise.all中,队列中有一个Promise实行失败,整个Promise.all都会进入失败回调。

state为1,则直接调用res(i, value)。

state状态为0,改promise成员直接调用then方法,获取其中value,再调用res(i, value)。

当队员成员Promise的value已经返回后,再次调用res(i, value),此时的value已经不是个Promise,将值赋到数组对应位子。

args[i] = val;
复制代码

所有Promise成员执行完毕后,resolve数组

if (--remaining === 0) {
    resolve(args);
  }
复制代码

Promise.resolve

Promise.resolve源码

var TRUE = valuePromise(true);
var FALSE = valuePromise(false);
var NULL = valuePromise(null);
var UNDEFINED = valuePromise(undefined);
var ZERO = valuePromise(0);
var EMPTYSTRING = valuePromise('');

function valuePromise(value) {
  var p = new Promise(Promise._noop);
  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;

  if (typeof value === 'object' || typeof value === 'function') {
    try {
      var then = value.then;
      if (typeof then === 'function') {
        return new Promise(then.bind(value));
      }
    } catch (ex) {
      return new Promise(function (resolve, reject) {
        reject(ex);
      });
    }
  }
  return valuePromise(value);
};
复制代码

这里用到了一个valuePromise的方法,这个方法接受一个值,而且设置一个新的Promise,将这个Promise的状态直接设置为1,value设置为传入值,直接返回这个Promise。

function valuePromise(value) {
  var p = new Promise(Promise._noop);
  p._state = 1;
  p._value = value;
  return p;
}
复制代码

Promise.all要分为3中状况分析:

1.传入一个值(如果对象或方法,则其中没有then的私有方法),那么直接将这个值传给valuePromise,并将其返回

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;
return valuePromise(value);
复制代码

用法上:

Promise.resolve(1).then((res) => {
    console.log(res) // 打印出1,Promise.resolve直接返回了一个新的Promise,它的值是1
})
复制代码

2.传入一个Promise,则直接将这个Promise返回。

f (value instanceof Promise) return value;
复制代码

3.传入一个含有then方法的对象或方法,会调用它的then方法。

try {
  var then = value.then;
  if (typeof then === 'function') {
    return new Promise(then.bind(value));
  }
} catch (ex) {
  return new Promise(function (resolve, reject) {
    reject(ex);
  });
}
复制代码

Promise.finally

then方法只在promise内方法执行成功时执行;catch方法只在Promise内方法执行失败了执行;有些时候无论方法体执行成功或失败,都要执行某些特定方法,这个时候就要使用finally。

示例代码4

new Promise((resolve) => {
    resolve(1);
}).then((res) => {
    ...
})
    catch((err) => {
        ...
    })
        .finally(() => {
            ...
        })
复制代码

源码中finally的代码以下:

Promise.prototype.finally = function (f) {
  return this.then(function (value) {
    return Promise.resolve(f()).then(function () {
      return value;
    });
  }, function (err) {
    return Promise.resolve(f()).then(function () {
      throw err;
    });
  });
};
复制代码

finally中只是返回了this.then,而后在then中的成功失败回调中都执行了传入的f方法。不过既然返回了this.then,也就意味着,finally后面能够继续链式调用then。

总结

Promise是一个构造函数,里面有2个核心的控制状态的变量:

  1. state主体方法执行的状态值,0表明初始值,1表明请求成功,2表明失败,3表明resolve了一个Promise实例
  2. defferedState是then执行的状态值,0表明then方法未执行,1表明执行了一个then方法,2表示then方法执行了屡次。

主要执行逻辑:

  1. 主体函数方法执行,通常是一个异步方法,执行完毕后若成功将state设置为1,失败设置为2;
  2. 检查defferedState值是否为0,如果0则等待then执行,不是则执行then中记录的回调。
  3. then方法执行,先设置一个Promise实例,并将其返回;将成功回调和失败回调记录下来,将defferedState设置为1。
  4. 检查state是否为0,为0则等待异步请求返回,1则执行成功方法,2执行失败方法。
  5. 将执行的回调返回值,用resolve的方式赋值给then中返回的Promise。
相关文章
相关标签/搜索