Promise.js 源码解析

Promise

MDN 的解释

The Promise object is used for deferred and asynchronous computations. A Promise represents an operation that hasn’t completed yet, but is expected in the future.

推迟异步指令,每一个 promise是一个未完成操作,你可以去指定对应的函数,去处理他操作成功或者失败 (throw error) 的情况

Promise 允许你以这样的形式来组织你的代码,一定程度上可以避免深层次 callback,虽然现在 es6 已经有 yield 等优雅的解了


let promise = new Promise((resolve, reject) => {
  // simulate ansynchronize work
  setTimeout(
    function() {
      // We fulfill the promise !
      resolve({ val: 'success' });
    }, Math.random() * 2000 + 1000)
})

promise
.then(val => {
  console.log('get value after success:', val)
})
.catch(err => {
  console.error('error occured after rejected:', err)
})

每个操作有两个状态 resolvereject,promise 中间会根据操作的情况,在内部进行状态机的转换,根据内部持有的代表状态的值,
来判断最后是把异步操作的结果 dispatch 给 resolve 函数,还是把 error/reason dispatch 给 reject 函数

Github 上一个支持 old version Node.js 和 browser 使用 Promise 的库 then/promise

Core Directory Tree

├── core.js
├── index.js
├── package.json
├── src
│   ├── core.js
│   ├── done.js
│   ├── es6-extensions.js
│   ├── finally.js
│   ├── index.js
│   ├── node-extensions.js
│   ├── rejection-tracking.js
│   └── synchronous.js

我们只分析比较重要的 core.js , done.jsfinally.js

Function Call Stack

先看 core.js

Comments 里面写了几个状态代表的含义

  • 0 -> pending 等待
  • 1 -> 履行结果,即满足 resolve 的要求
  • 2 -> 拒绝,代表 promise 遇到了 error 或者其他问题
  • 3 -> 把 state 转给下一个 promise

我画了一个简单的 call graph,可以看到 promise 被创建后的执行流程,如果你觉得复杂,
可以只看下面这个总结图

总结

promise -> doResolve

        promise.then
             |
             v
          handle <--------------+
             |                  |
             v                  |
      handleResolved            |
             |                  |
             v                  |
      resolve / reject          |
             |                  |
             v                  |
newValue has then function      |
             |                  |
            / \                 |
         y /   \ n              |
  doResolve     finale  --------+

其实后面很多步主要是关于 newValue 是不是 Promise而继续进行的

也就是说你创建 Promise 的时候,doResolve 执行,如果 then 返回的不是 promise,基本上就结束了

那再看看详细的吧,嫌多可以跳过

Promise

  Promise
      |
      v
(if fn !== noop)
      |
      v
  doResolve

Promise.then

  Promise.then <--------------+
        |                     |
        v                     |
   (is Promise)               |
        |                     |
       / \                    |
handle    safeThen            |
             |                |
             v                |
        (new Promise).then ---+

handle

    handle
      |
      v
  _state ----------------+
      |  \               |
      |   \              |
    3 |    \             |
  assign    0            |
            |            |
            v            |
    _deferredState       |
          /    \         |
         0      1        |
         |      |        |
         v      v        |
  _dS = 1      _dS = 2   |
         \      |       /
          \     |      /
           \    |     /
            \   v    /
            handleResolved

handleResolved

      handleResolved
           |
           v
         asap
           |
           v
        _state
         /   \
        /     \
       1        2
       |        |
       v        v
onFulfilled    onRejected

         cb
          |
        /   \
       /     \
    null    tryCallOne
     |         |
     v         v
_state         ret
     |         |
      \        /
       \      /
        \    /
  resolve or reject

resolve

    resolve
        |
     newValue
     /     \
Promise   function
   |         |
   v         v
finale     doResolve


_state = 1
_value = newValue

reject

_state = 2
_value = newValue

reject -> finale

Important Implementation

主要的代码都在 core.js

当然还有额外的两个文件里为 Promise 添加了 donefinally 的 prototype 函数

我们就重点来看一下代码吧,反正200行,我不客气粘在这里了。。。

第一部分,辅助函数、辅助变量 和 状态说明

我将把自己的注释的内容添以// <---的形式加在里面

'use strict';

var asap = require('asap/raw');

function noop() {}

// States:
//
// 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.


// to avoid using try/catch inside critical functions, we
// extract them to here.
var LAST_ERROR = null; // <--- 最后一个错误,用来做 traceback 的
var IS_ERROR = {}; // <--- 枚举变量,代表有错误

// <--- 获取 then 函数

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

// <--- 基本上就是包含 resolve 和 reject 两个参数的一个 promise call 的 wrapper,在
// <--- doResolve 里调用,为了catch promise 执行动作中遇到的错误

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

// <--- 来看这里 doResolve 其实就是当 fn 这个函数执行有 error 的时候,进行 reject

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

下面来看看 Promise 的定义

module.exports = Promise;

function Promise(fn) {
  if (typeof this !== 'object') {
    throw new TypeError('Promises must be constructed via new');
  }
  if (typeof fn !== 'function') {
    throw new TypeError('not a function');
  }
  this._deferredState = 0; // <--- deffer 状态,后面解释
  this._state = 0; // <--- 状态机维护的状态
  this._value = null; // <--- resolve 返回的结果
  this._deferreds = null; // <--- 一个数组,保存 deffer 的结果,后面解释
  if (fn === noop) return;
  doResolve(fn, this);
}
Promise._onHandle = null;
Promise._onReject = null;
Promise._noop = noop;

紧接着是他的一些 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 把 this 指针、两个回调函数和一个新的空 Promise 函数,传给了 handle,
Handler 定义如下

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

handle 函数主要处理 _state,当 self._state = 3 的时候,证明传给了下一个promise,然后就用一个 while (self._state === 3) 来不断把 self._value 赋值给 self,代表把 this 指针交给 _value (就是下一个 promise)

function handle(self, deferred) {
  while (self._state === 3) {
    self = self._value;
  }
  if (Promise._onHandle) {
    Promise._onHandle(self);
  }
  if (self._state === 0) { // <--- 当状态为 pending 的时候
    if (self._deferredState === 0) {
      self._deferredState = 1; // <--- 如果 deferredState 为 0,改为1
      self._deferreds = deferred; // <--- 此时 deferreds 为唯一的一个 Handler
      return;
    }
    if (self._deferredState === 1) {
      self._deferredState = 2;
      self._deferreds = [self._deferreds, deferred];
      return;
    }
    self._deferreds.push(deferred);
    return;
  }
  handleResolved(self, deferred); // <--- 这个只有在 _state 是1或者2的时候才会被调用
}

那我们先看看 _state 在哪里被改变状态,搜了一下是 resolve 和 reject,先看他们

resolve 实现是这样的

function resolve(self, newValue) {
  // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  if (newValue === self) { // <--- resolve 的值不能是自己
    return reject(
      self,
      new TypeError('A promise cannot be resolved with itself.')
    );
  }
  // <--- newValue 不能是 null 或 undefined,而且必须是一个 function 或者 object
  if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
    var then = getThen(newValue);
    if (then === IS_ERROR) {
      return reject(self, LAST_ERROR);
    }
    // <--- 那么当 newValue 还是一个 Promise 的时候,then 也是 Promise 的 then 函数
    if (then === self.then && newValue instanceof Promise) {
      self._state = 3; // <--- 交给下一个 promise
      self._value = newValue;
      finale(self); // <--- invoke finale
      return;
    } else if (typeof then === 'function') {
      doResolve(then.bind(newValue), self);
      return;
    }
  }
  self._state = 1;
  self._value = newValue;
  finale(self); // 这是其他情况,应该就是返回的是值 (string, number...) 吧,resolve 结果
}

reject 如果跳出条件的话非常简单,直接修改状态,invoke finale
还有一种是在条件内,then 是function,但 newValue 不是 Promise,直接走 doResolve

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

call stack 走到了 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(self, self._deferreds[i]);
    }
    self._deferreds = null;
  }
}

遇到一个问题:什么地方让 _deferredState 的状态改变?什么地方让 _deferreds 变成数组?

没错,是 handle,pending 状态中,会改变 _deferreds 的值,_deferredState 会0变成1,1变成2
_deferredState 和 _state 不一样,他代表的应该是 _deferreds 的类型,1 是 single,2 是 Array

来看看 _deferreds 里面每次存的值到底是什么情况

次数 描述 _deferreds 的值
第一次 初始化,_deferredState 为 0 null
第二次 _deferredState 变成 1 deferred,一个 new Handler
第三次 _deferredState 变成 2 [self._deferreds, deferred]

做完都 return 了,但是 第三次的之前经历了 _state = 1 的情况,
会执行handleResolved,即 resolve -> finale -> handle -> handleResolved,看到没,终于到了

这里判断了状态是 resolve 还是 reject,然后选取对应的 callback
如果 callback 为 null,那么 resolve 之前 new Handler 里保存的 promise,并带上 _value
如果 callback 有赋值,执行一次 cb(self._value),看是不是有错误,若没抛出错误,把返回值替代 _value 拿去 resolve
错误处理就不说了

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

handleResolved 里依赖了一个叫 asap 的 high priority queue,完成了 promise 任务的队列调度,The asap function executes a task as soon as possible but not before it returns, waiting only for the completion of the current event and previously scheduled tasks.(你可以大概理解为:把要做的 task 当做一个异步任务来做,asap 的 callback 里处理完成后的结果)

发现 resolve 里有一行注释

https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure

# description
An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.

找到了一个 Promise A+ 的网站,经评论区同学提醒,这是 Promise A+ 标准,一个浏览器 promise 的开放标准,包含许多对 promise 的细节定义

An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.

To run [[Resolve]](promise, x), perform the following steps:

  1. 如果 promisex 引用的同一个对象,reject promise 一个 TypeError 作为原因
  2. 如果 x 是一个 promise 对象,接收它的 state
    1. 如果 x 在 pending 状态,promise 必须保持 pending 直到 x 满足 fullfiled 或者 rejected
    2. 如果或当 x 满足 fulfilled,以相同的值 fullfil promise,就是 resolve
      如果或当 x 被 rejected,以相同的值 reject promise
  3. 否则, 如果 x 是一个对象或者函数,
    resolve 里做的判断
    1. then = x.then.
    2. 如果检索 x.then 这个属性的时候抛出异常 e,那么以e作为 reason 来 reject promise
    3. 如果 then 是一个函数,用 x 的 this 指针去调用它,第一个参数是 reslovePromise,第二个参数是 rejectPromise,当:
      这就是 handle 调用 doResolve 的情况,更改 this 指针
      1. 如果 resolvePromise 以一个y参数的回调执行,执行 [[Resolve]](promise, y)
      2. 如果 rejectPromise 以一个r参数的回调执行,用 reason r 去 reject promise
      3. 如果两个回调都被执行或者多次调用,只让第一个生效,其余忽略 这就是 done flag 的作用
      4. If calling then throws an exception e,
      5. 如果调用 then 的时候出现了异常 e,
        1. 如果 resolvePromise or rejectPromise 已经被调用了,忽略它
        2. 否则,用 e 去 reject promise
    4. 如果 then 是一个函数,fulfill promise with x
  4. 如果 x 不是 Object 也不是函数,依旧用 x fulfill promise

那这次的解析就到这里,如果有问题,欢迎指正