专栏首页魂祭心原 Promise 实现

原 Promise 实现

Introduction

本文最初来源于 Stack Overflow的一个回答,探讨如同使用javascript实现一个promise,你也能通过阅读本文更深入的理解promise的实现机制。

State Machine

因为promise是一个状态机,因而我们可以先考虑之后会用到哪些状态信息。

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise() {
  // store state which can be PENDING, FULFILLED or REJECTED
  var state = PENDING;

  // store value or error once FULFILLED or REJECTED
  var value = null;

  // store sucess & failure handlers attached by calling .then or .done
  var handlers = [];
}

Transitions

然后在考虑两个重要的状态变化, fulfilling 和rejecting

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise() {
  // store state which can be PENDING, FULFILLED or REJECTED
  var state = PENDING;

  // store value once FULFILLED or REJECTED
  var value = null;

  // store sucess & failure handlers
  var handlers = [];

  function fulfill(result) {
    state = FULFILLED;
    value = result;
  }

  function reject(error) {
    state = REJECTED;
    value = error;
  }
}

这段代码是一个基础的状态变化,之外还有其他的更加高级的状态变化叫做resolve

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise() {
  // store state which can be PENDING, FULFILLED or REJECTED
  var state = PENDING;

  // store value once FULFILLED or REJECTED
  var value = null;

  // store sucess & failure handlers
  var handlers = [];

  function fulfill(result) {
    state = FULFILLED;
    value = result;
  }

  function reject(error) {
    state = REJECTED;
    value = error;
  }

  function resolve(result) {
    try {
      var then = getThen(result);
      if (then) {
        doResolve(then.bind(result), resolve, reject)
        return
      }
      fulfill(result);
    } catch (e) {
      reject(e);
    }
  }
}

这里resolve接受promise和一个普通值,如果是个promise,需要执行这个promise。A promise must never be fulfilled with another promise, so it is this resolve function that we will expose, rather than the internal fulfill. We've used a couple of helper methods, so lets define those:

/**
 * Check if a value is a Promise and, if it is,
 * return the `then` method of that promise.
 *
 * @param {Promise|Any} value
 * @return {Function|Null}
 */
function getThen(value) {
  var t = typeof value;
  if (value && (t === 'object' || t === 'function')) {
    var then = value.then;
    if (typeof then === 'function') {
      return then;
    }
  }
  return null;
}

/**
 * Take a potentially misbehaving resolver function and make sure
 * onFulfilled and onRejected are only called once.
 *
 * Makes no guarantees about asynchrony.
 *
 * @param {Function} fn A resolver function that may not be trusted
 * @param {Function} onFulfilled
 * @param {Function} onRejected
 */
function doResolve(fn, onFulfilled, onRejected) {
  var done = false;
  try {
    fn(function (value) {
      if (done) return
      done = true
      onFulfilled(value)
    }, function (reason) {
      if (done) return
      done = true
      onRejected(reason)
    })
  } catch (ex) {
    if (done) return
    done = true
    onRejected(ex)
  }
}

Constructing

我们现在有了完整的内部状态机, 但我们还没有一个处理promise或观察它的方法。让我们开始添加resolve方法。

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise(fn) {
  // store state which can be PENDING, FULFILLED or REJECTED
  var state = PENDING;

  // store value once FULFILLED or REJECTED
  var value = null;

  // store sucess & failure handlers
  var handlers = [];

  function fulfill(result) {
    state = FULFILLED;
    value = result;
  }

  function reject(error) {
    state = REJECTED;
    value = error;
  }

  function resolve(result) {
    try {
      var then = getThen(result);
      if (then) {
        doResolve(then.bind(result), resolve, reject)
        return
      }
      fulfill(result);
    } catch (e) {
      reject(e);
    }
  }

  doResolve(fn, resolve, reject);
}

如你所见,这里多次调用doResolve,因为调用实在不同的resolve上面发生的,fn可以多次调用resolve和reject,甚至抛出异常,必须确保promise只能resolved或者reject一次,then也不能转变成之前的状态。

Observing (via .done)

现在已经完成了这个状态机,但是仍旧缺乏手段观察到内部的变化,我们最终的目标是实现then,但是done要更加好些,因而首先来实现done。

.done需要实现的功能

  1. 只有当onFulfilled或者onRejected发生时
  2. 只能被调用一次
  3. 直到下一次调用(即在返回方法返回后)才调用它。
  4. 确保在resilve之前或者之后done都要被调用
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise(fn) {
  // store state which can be PENDING, FULFILLED or REJECTED
  var state = PENDING;

  // store value once FULFILLED or REJECTED
  var value = null;

  // store sucess & failure handlers
  var handlers = [];

  function fulfill(result) {
    state = FULFILLED;
    value = result;
    handlers.forEach(handle);
    handlers = null;
  }

  function reject(error) {
    state = REJECTED;
    value = error;
    handlers.forEach(handle);
    handlers = null;
  }

  function resolve(result) {
    try {
      var then = getThen(result);
      if (then) {
        doResolve(then.bind(result), resolve, reject)
        return
      }
      fulfill(result);
    } catch (e) {
      reject(e);
    }
  }

  function handle(handler) {
    if (state === PENDING) {
      handlers.push(handler);
    } else {
      if (state === FULFILLED &&
        typeof handler.onFulfilled === 'function') {
        handler.onFulfilled(value);
      }
      if (state === REJECTED &&
        typeof handler.onRejected === 'function') {
        handler.onRejected(value);
      }
    }
  }

  this.done = function (onFulfilled, onRejected) {
    // ensure we are always asynchronous
    setTimeout(function () {
      handle({
        onFulfilled: onFulfilled,
        onRejected: onRejected
      });
    }, 0);
  }

  doResolve(fn, resolve, reject);
}

确保当Promise resolved 或者rejected时 handlers 能够执行而不是等到下一个next tick

Observing (via .then)

现在已经完成了done,那么可以用同样的方式实现then,区别在于重新构造一个新的Promise

this.then = function (onFulfilled, onRejected) {
  var self = this;
  return new Promise(function (resolve, reject) {
    return self.done(function (result) {
      if (typeof onFulfilled === 'function') {
        try {
          return resolve(onFulfilled(result));
        } catch (ex) {
          return reject(ex);
        }
      } else {
        return resolve(result);
      }
    }, function (error) {
      if (typeof onRejected === 'function') {
        try {
          return resolve(onRejected(error));
        } catch (ex) {
          return reject(ex);
        }
      } else {
        return reject(error);
      }
    });
  });
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 原 js判断旋转中的图片里的元素与背景的某

    魂祭心
  • 原 ionic+js+html5 飞行射击

    魂祭心
  • 原 Curry的js实现

    魂祭心
  • 如何使用async和await这对组合设计统一的取Access Token的函数

    最近我在使用SAP云平台的机器学习API做和SAP系统的集成,因为SAP Cloud Platform Leonardo上的机器学期API,每次消费时需要传一个...

    Jerry Wang
  • js中的变量声明问题

    从1,,2中我们可以看出js引擎是先对var声明的变量进行注册,再对函数类型的变量进行注册。 而3和4是一样的原理,js引擎执行到这段代码时,首先注册var a...

    theanarkh
  • 简单的实现Javascript的MVC

    最近看了一篇文章,“30行代码实现Javascript中的MVC”,原文链接:http://www.jqsite.com/notes/1603205925.ht...

    IMWeb前端团队
  • 简单的实现Javascript的MVC

    最近看了一篇文章,“30行代码实现Javascript中的MVC”,原文链接:http://www.jqsite.com/notes/1603205925.ht...

    IMWeb前端团队
  • JS变量声明提升解释

    来说一下函数表达式,var C 与 function C 都是声明语句,区别在于 var C 是函数表达式,而 function C 是函数声明。

    无邪Z
  • nodejs实现批量修改文件内容 | 附断更红包

    一番之前不是有一个网站吗,efonfihgint.imwork.net。但这是个二级域名,很多时候用起来不方便,自主性还是稍差了一点。

    efonfighting
  • 说下js中的bind

    bind的受体是对象,返回的是个新的函数。 我们知道this总是指向调用他的对象。但是有时候我们希望‘固化’这个this。 也就是无论怎么调用这个返回的函数...

    mafeifan

扫码关注云+社区

领取腾讯云代金券