手写 Promise 符合 Promises/A+规范

异步编程是前端开发者必需的技能,过去管理异步的主要机制都是经过函数回调,然而会出现像“回调地狱”这样的问题。为了更好的管理回调,ES6 增长了一个新的特性 PromisePromiseES7async/await 语法的基础,是 JavaScript 中处理异步的标准形式,现实开发中基本离不开 Promise 了。咱们接下来会根据 Promises/A+ 规范本身实现一个 Promise前端

完整的代码能够点击个人 github 进行查看,若是你喜欢,欢迎 star,若是发现有问题或者错误,也欢迎提出来。git

Promises/A+ 规范

首先咱们来看 Promises/A+ 规范的具体内容,Promises/A+ 规范能够查看 Promises/A+,下面是翻译的规范供参考github

术语npm

  1. promise 是一个有 then 方法的对象或者是函数,行为遵循本规范
  2. thenable 是一个有 then 方法的对象或者是函数
  3. valuepromise 状态成功时的值,包括 undefinedthenablepromise
  4. exception 是一个使用 throw 抛出的异常值
  5. reasonpromise 状态失败时的值

要求编程

2.1 Promise 状态

Promise 必须处于如下三个状态之一: pending, fulfilled 或者 rejectedpromise

2.1.1 若是 promise 在 pending 状态
    2.1.1.1 能够变成 fulfilled 或者是 rejected
2.1.2 若是 promise 在 fulfilled 状态
    2.1.2.1 不会变成其它状态
    2.1.2.2 必须有一个value值
2.1.3 若是 promise 在 rejected 状态
    2.1.3.1 不会变成其它状态
    2.1.3.2 必须有一个 promise 被 reject 的 reason 
复制代码

2.2 then 方法

promise 必须提供一个 then 方法,来访问最终的结果缓存

promisethen 方法接收两个参数微信

promise.then(onFulfilled, onRejected)
复制代码
2.2.1 onFulfilled 和 onRejected 都是可选参数:
    2.2.1.1 onFulfilled 必须是函数类型
    2.2.1.2 onRejected 必须是函数类型
2.2.2 若是 onFulfilled 是函数:
    2.2.2.1 必须在 promise 变成 fulfilled 时,调用 onFulfilled,参数是 promise 的 value
    2.2.2.2 在 promise 的状态不是 fulfilled 以前,不能调用
    2.2.2.3 onFulfilled 只能被调用一次
2.2.3 若是 onRejected 是函数:
    2.2.3.1 必须在promise变成 rejected 时,调用 onRejected,参数是promise的reason
    2.2.3.2 在promise的状态不是 rejected 以前,不能调用
    2.2.3.3 onRejected 只能被调用一次
2.2.4 onFulfilled 和 onRejected 应该是微任务
2.2.5 onFulfilled 和 onRejected 必须做为函数被调用
2.2.6 then 方法可能被屡次调用
    2.2.6.1 若是 promise 变成了 fulfilled 态,全部的 onFulfilled 回调都须要按照 then 的顺序执行
    2.2.6.2 若是 promise 变成了 rejected 态,全部的 onRejected 回调都须要按照 then 的顺序执行
2.2.7 then 必须返回一个 promise, promise2 = promise1.then(onFulfilled, onRejected);
    2.2.7.1 onFulfilled 或 onRejected 执行的结果为 x, 执行 resolutionProcedure(promise2, x) 
    2.2.7.2 若是 onFulfilled 或者 onRejected 执行时抛出异常e, promise2 须要被 reject
    2.2.7.3 若是 onFulfilled 不是一个函数,promise2 以 promise1 的值 fulfilled
    2.2.7.4 若是 onRejected 不是一个函数,promise2 以 promise1 的 reason rejected
复制代码

2.3 The Promise Resolution Procedure

resolutionProcedure(promise2, x, resolve, reject)
复制代码
2.3.1 若是 promise2 和 x 相等,那么 promise 执行 reject TypeError
2.3.2 若是 x 是一个 promsie
    2.3.2.1 若是 x 是 pending 状态,那么 promise 必需要保持 pending 状态直到 x 变成 fulfilled 或者 rejected 状态
    2.3.2.2 若是 x 是 fulfilled 状态, fulfill promise with the same value
    2.3.2.3 若是 x 是 rejected 状态, reject promise with the same reason
2.3.3 若是 x 是一个 object 或者 是一个 function
    2.3.3.1 let then = x.then.
    2.3.3.2 若是 x.then 这步出错,那么 reject promise with e as the reason
    2.3.3.3 若是 then 是一个函数,then.call(x, resolvePromise, rejectPromise)
        2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolutionProcedure(promise2, y, resolve, reject);
        2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r.
        2.3.3.3.3 若是 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
        2.3.3.3.4 若是调用then抛出异常 e
            2.3.3.3.4.1 若是 resolvePromise 或 rejectPromise 已经被调用,那么忽略
            2.3.3.3.4.2 不然,reject promise with e as the reason
    2.3.3.4 若是 then 不是一个function. fulfill promise with x.
2.3.4 若是 x 不是一个 object 或者 function,fulfill promise with x.
复制代码

Promise 源码实现

接下来咱们只要按照规范来实现 Promise 就好了,很显然规范后半部分看起来是比较复杂的,尤为是 resolutionProcedure 函数的实现,但其实这只是一些实现的细节而已。初看可能不是那么顺畅,那么强烈建议多看几遍规范,而后本身多实现几遍。异步

  1. promise 须要传递一个 executor 执行器,执行器马上执行async

  2. 执行器 executor 接受两个参数,分别是 resolvereject

  3. promise 只能从 pendingrejected, 或者从 pendingfulfilled,状态一旦确认,就不会再改变

  4. promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected

  5. 若是调用 then 时,promise 已经成功,则执行 onFulfilled,并将 promise 的值做为参数传递进去。 若是 promise 已经失败,那么执行 onRejected, 并将 promise 失败的缘由做为参数传递进去。若是 promise 的状态是 pending,须要将 onFulfilledonRejected 函数存放起来,等待状态肯定后,再依次将对应的函数执行。

  6. then 的参数 onFulfilledonRejected 能够缺省

  7. promise 能够 then 屡次,promisethen 方法返回一个新的 promise

  8. 若是 then 返回的是一个结果,那么就会把这个结果做为参数,传递给下一个 then 的成功的回调 onFulfilled

  9. 若是 then 中抛出了异常,那么就会把这个异常做为参数,传递给下一个 then 的失败的回调 onRejected

  10. 若是 then 返回的是一个 promise, 那么须要等这个 promise,那么会等这个 promise 执行完。promise 若是成功, 就走下一个 then 的成功,若是失败,就走下一个 then 的失败

// 定义三种状态的常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

// pomise 接收一个 executor 执行器,执行器马上执行
function MyPromise(executor) {
  const _this = this;
  // promise 当前的状态
  _this.currentState = PENDING;
  _this.value = undefined;

  // 保存 then 中的回调,只有当 promise 状态为 pending 时才会缓存,而且每一个实例至多缓存一个
  _this.onFulfilledCallbacks = [];
  _this.onRejectedCallbacks = [];


  function resolve(value) {
    if (value instanceof MyPromise) {
      // 若是 value 是个 Promise,调用 then 方法继续执行
      value.then(resolve, reject);
    }

    // 异步执行,保证执行顺序
    setTimeout(() => {
      if (_this.currentState === PENDING) {
        _this.currentState = FULFILLED;
        _this.value = value;
        _this.onFulfilledCallbacks.forEach(fn => fn());
      }
    });
  }

  function reject(reason) {
    // 异步执行,保证执行顺序
    setTimeout(() => {
      if (_this.currentState === PENDING) {
        _this.currentState = REJECTED;
        _this.value = reason;
        _this.onRejectedCallbacks.forEach(fn => fn());
      }
    });
  }

  try {
    executor(resolve, reject);
  } catch(err) {
    reject(err);
  }
}

MyPromise.prototype.constructor = MyPromise;

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  const _this = this;
  // 2.2.1 onFulfilled 和 onRejected 都是可选参数
  // 2.2.5 onFulfilled 和 onRejected 必须做为函数被调用
  // 2.2.7.3 若是 onFulfilled 不是一个函数,promise2 以promise1的值fulfilled
  // 2.2.7.4 若是 onRejected 不是一个函数,promise2 以promise1的reason rejected
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : valve => valve;
  onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

  // 2.2.7,then 必须返回一个新的 promise
  const promise2 = new MyPromise((resolve, reject) => {
    if (_this.currentState === FULFILLED) {
      // 2.2.4 保证 onFulfilled,onRjected 异步执行
      setTimeout(() => {
        try {
          // 2.2.7.1 onFulfilled 或 onRejected 执行的结果为 x, 调用 resolutionProcedure
          const x = onFulfilled(_this.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(err) {
          // 2.2.7.2 若是 onFulfilled 或者 onRejected 执行时抛出异常 err, promise2 须要被 reject
          reject(err);
        }
      });
    }
    
    if (_this.currentState === REJECTED) {
      setTimeout(() => {
        try {
          // 2.2.7.1 onFulfilled 或 onRejected 执行的结果为 x, 调用 resolutionProcedure
          const x = onRejected(_this.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(err) {
          // 2.2.7.2 若是 onFulfilled 或者 onRejected 执行时抛出异常 err, promise2 须要被 reject
          reject(err);
        }
      });
    }
    
    if (_this.currentState === PENDING) {
      _this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onFulfilled(_this.value);
            resolutionProcedure(promise2, x, resolve, reject);
          } catch(err) {
            reject(err);
          }
        });
      });

      _this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onRejected(_this.value);
            resolutionProcedure(promise2, x, resolve, reject);
          } catch(err) {
            reject(err);
          }
        });
      });
    }
  });

  return promise2;
}

// 2.3 resolutionProcedure(promise2, x, resolve, reject)
function resolutionProcedure(promise2, x, resolve, reject) {

  // 2.3.1 若是 promise2 和 x 相等,那么 reject promise with a TypeError
  if (promise2 === x) {
    reject(new TypeError('Error'));
  }

  // 2.3.2 若是 x 为 Promise,状态为 pending 须要继续等待不然执行
  if (x instanceof MyPromise) {
    if (x.currentState === PENDING) {
      x.then(value => {
        resolutionProcedure(promise2, value, resolve, reject);
      }, reject)
    } else {
      x.then(resolve, reject);
    }
  }

  // 2.3.3.3.3 reject 或者 resolve 其中一个执行过的话,忽略其余的
  let called = false;

  // 2.3.3,判断 x 是否为对象或者函数
  if ( x && (typeof x === 'object' || typeof x === 'function') ) {
    try {
      let then = x.then;
      // 2.3.3.3 若是 then 是一个函数,then.call(x, resolvePromise, rejectPromise)
      if (typeof then === 'function') {
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            // 2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolutionProcedure(promise2, y, resolve, reject);
            resolutionProcedure(promise2, y, resolve, reject);
          },
          r => {
            if (called) return;
            called = true;
            // 2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r.
            reject(r);
          }
        );
      } else {
        // 2.3.3.4 若是 then 不是一个 function. fulfill promise with x.
        resolve(x);
      }
    } catch(err) {
      // 2.3.3.2 若是 x.then 这步出错,那么 reject promise with err as the reason
      if (called) return;
      called = true;
      reject(err)
    }

  } else {
    // 2.3.4 若是 x 不是一个 object 或者 function,fulfill promise with x.
    resolve(x);
  }
}

module.exports = MyPromise;
复制代码

测试

promises-aplus-tests 这个 npm 包能够帮助咱们测试所编写的 promise 代码是否符合 Promises/A+ 的规范。

不过咱们须要先增长如下代码去提供测试的接口

MyPromise.defer = MyPromise.deferred = function () {
  let dfd = {};
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
}
复制代码

经过 npm 安装

npm install promises-aplus-tests --save-dev
复制代码

经过执行 promises-aplus-tests target.js 能够执行测试,promises-aplus-tests 中共有 872 条测试用例。

能够在个人 github 进行查看,经过 npm run test 来执行测试,能够经过全部的测试用例。

更多精彩内容,欢迎关注微信公众号~

相关文章
相关标签/搜索