精华 简单实现Promise/A+. 对着文档 .含注释
发布于 2 年前 作者 William17 4793 次浏览 最后一次编辑是 7 个月前 来自 分享

阅读指南

(function() {
  'use strict';

  // 用于异步执行 onFulfilled/onRejected
  // `setImmediate` or `function(fn) { setTimeout(fn, 0) }` in browser
  // `process.nextTick` in node
  var asyncCall = process.nextTick;

  // 2.3
  // Promise解析过程 是以一个promise和一个值做为参数的抽象过程,
  // 可表示为
  // [[Resolve]](promise, x)
  function resolve(promise, x) {
    // 2.3.1
    // 如果promise 和 x 指向相同的值,
    // 使用 TypeError做为原因将promise拒绝
    if (promise === x) {
      return promise.reject(new TypeError('The promise and its value refer to the same object'));
    }
    // 2.3.3
    // 如果x是一个对象或一个函数
    if (x && (typeof x === 'function' || typeof x === 'object')) {
      // 2.3.3.3
      // 如果 resolvePromise 和 rejectPromise 都被调用了,
      // 或者被调用了多次,则只第一次有效,后面的忽略
      // // 我们用 called 作为标识防止被多次调用
      var called = false,
        then;

      try {
        // 2.3.3.1
        // 将 then 赋为 x.then
        then = x.then;

        if (typeof then === 'function') {
          // 2.3.3.3
          // 如果 then 是一个函数,
          // 以x为this调用then函数,
          // 且第一个参数是resolvePromise,
          // 第二个参数是rejectPromise
          then.call(x, function(y) {
            // 2.3.3.3.1
            // 当 resolvePromise 被以 y为参数调用,
            // 执行 [[Resolve]](promise, y)
            if (!called) {
              called = true;
              resolve(promise, y);
            }
          }, function(r) {
            // 2.3.3.3.2
            // 当 rejectPromise 被以 r 为参数调用,
            // 则以r为原因将promise拒绝。
            if (!called) {
              called = true;
              promise.reject(r);
            }
          });
        }else {
          // 2.3.3.4
          // 如果 then不是一个函数,则 以x为值fulfill promise
          promise.fulfill(x);
        }
      }catch (e) {
        // 2.3.3.2
        // 如果在取x.then值时抛出了异常,
        // 则以这个异常做为原因将promise拒绝
        if (!called) {
          called = true;
          promise.reject(e);
        }
      }
    }else {
      // 2.3.4
      // 如果 x 不是对象也不是函数,
      // 则以x为值 fulfill promise
      promise.fulfill(x);
    }
  }

  function Taxi() {
    // 0 pending, 1 fulfilled, 2 rejected
    var _state = 0,
      _value,
      _onFulfills = [],
      _onRejects = [];
    this.done = function(onFulfilled, onRejected) {
      
      if (_state === 0) {
        // 如果还在pending,先把处理函数存起来
        _onFulfills.push(onFulfilled);
        _onRejects.push(onRejected);
      }else {
        // 否则,异步执行
        asyncCall(function() {
          if (_state === 1) {
            if (typeof onFulfilled === 'function') {
              onFulfilled(_value);
            }
          }else if (typeof onRejected === 'function') {
            onRejected(_value);
          }
        });
      }
    };

    /**
     * 用于this.fulfill和this.reject内部调用的函数
     * @param  {number} state 0->pending, 1->fulfill, 2->reject
     * @param  {dynamic} value result 或 reason
     */
    function _complete(state, value){
      // 只能 fulfill或reject一次, 后面的忽略
      if (!_state) {
        _state = state;
        _value = value;
        // 根据 state 获取需要处理的函数数组
        // 异步执行
        asyncCall(function() {
          var handlers = state == 1 ? _onFulfills : _onRejects;
          handlers.forEach(function(fn) {
            if (typeof fn === 'function') {
              fn(value);
            }
          });
          // 执行完之后,解除数组引用
          _onFulfills = null;
          _onRejects = null;
        });
      }
    }
    this.fulfill = function(value) {
      _complete(1, value);
    };
    this.reject = function(value) {
      _complete(2, value);
    };
  }

  Taxi.prototype = {
    constructor: Taxi,
    catch: function(onRejected) {
      this.then(null, onRejected);
    },
    then: function(onFulfilled, onRejected) {
      // 2.2.7
      // then 必须返回一个promise
      // 所以我们new一个,等下用于返回
      var taxi = new Taxi();

      // this指向当前promise
      // 2.2.2
      // 如果onFulfilled是一个函数:
      // 它必须在promise fulfilled后调用, 且promise的value为其第一个参数。
      // 2.2.3
      // 如果onRejected是一个函数,
      // 它必须在promise rejected后调用, 且promise的reason为其第一个参数。
      this.done(function(x) {
        if (typeof onFulfilled === 'function') {
          try {
            // 2.2.7.1
            // 如果onFulfilled 或 onRejected 返回了值x,
            // 则执行Promise 解析流程[[Resolve]](promise2, x).
            resolve(taxi, onFulfilled(x));
          }catch (e) {
            // 2.2.7.2
            // 如果onFulfilled 或 onRejected抛出了异常e,
            // 则promise2应当以e为reason被拒绝
            taxi.reject(e);
          }
        }else {
          // 2.2.7.3
          // 如果 onFulfilled 不是一个函数且promise1已经fulfilled,
          // 则promise2必须以promise1的值fulfilled.
          taxi.fulfill(x);
        }
      }, function(x) {
        
        if (typeof onRejected === 'function') {
          try {
            // 2.2.7.1
            // 如果onFulfilled 或 onRejected 返回了值x,
            // 则执行Promise 解析流程[[Resolve]](promise2, x).
            resolve(taxi, onRejected(x));
          }catch (e) {
            // 2.2.7.2
            // 如果onFulfilled 或 onRejected抛出了异常e,
            // 则promise2应当以e为reason被拒绝
            taxi.reject(e);
          }
        }else {
          // 2.2.7.4
          // 如果 OnReject 不是一个函数且promise1已经rejected,
          // 则promise2必须以相同的reason被拒绝.
          taxi.reject(x);
        }
      });
      return taxi;
    }
  };
  module.exports = Taxi;
}());

12 回复

精神可嘉,精华

真棒~ Awesome Promise 上有Lie等简单实现, 如果不先看 specifications 的话, 实现看起来还真是一头雾水~

@William17 每次 都 var taxi = new Taxi(); 。 这样的话 _onFulfills = [], _onRejects = []; 里面不都是一个函数吗,用数组干嘛? 为什么每次都要new ?

@wdragon1983 这些是规范规定的.

  • then必须返回一个Promise对象, 这样的话就可以像下面那样链式操作, 另外,这个对象当然应该是一个新的对象
p.then(fn1).then(fn2);
  • 上面那种操作第二个then是新的Promise对象的. 如果要使用一开始的对象的then, 那就直接调用, 下面这种情况就可能出现多个fulfill或reject回调
p.then(fn1);
p.then(fn2);
p.then(fn3);
p.reject(fn4);

举一个例子, 如果你有几个模块都需要另外一个东西(例如websocket连接?), 像下面, 这样你可以把模块的代码分开, 更好维护

// 模块1
socketConnect.then(init);
// 模块2
socketConnect.then(init);

@William17 那意思就是 如果p.then(fn1).then(fn2); 这种调用方式 。_onFulfills = [], _onRejects = []; 其实就是一个 值得数组。 p.then(fn1); p.then(fn2); p.then(fn3); p.reject(fn4); 这种调用才会出现多个值得数组。

不过还是看的云里雾里,resolve方法里面不知道在干嘛。

@wdragon1983 确实是那样 resolve方法, 大概可以这么理解, 就是当一个promise被一个值resolve的时候, 假设这个值是x, 因为x有可能是任何东西, 函数, 对象, 简单的原始值等等都有可能, 你可以看到resolve里很多if, 就是分情况处理, 你把各种情况理清楚了, 就明白了

回到顶部