Toc
  1. 关于 Promise
  2. 关于 Promise A+
  3. 基于 Promise A+的代码实现
    1. 构造函数
    2. resolve/reject 实现
    3. then 方法实现
    4. resolvePromise
    5. 代码测试
    6. 总结
Toc
0 results found
FBB
Promise的分析与实现
2020/05/19 前端 JS

Promise 作为一个异步的实现方案,几乎成为了面试的必考点。在了解了基本使用的前提下,还需要知道它底层的实现原理。所以本文的最后会实现一个基于 Promise A+规范的 Promise,最后会根据 Promise A+的官网进行测试。

关于 Promise

Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件–更合理和更强大。

关于 Promise 的使用,这里就不过多解释。看一看阮一峰大大的Promise 对象

关于 Promise A+

关于 Promise A+规范,这里也不做过多分析。
Promise A+规范原文链接点击这里。
由于原文是英文,笔者英语并不是很好,所以再网上找了翻译放在这里。
翻译 1/翻译 2

基于 Promise A+的代码实现

先来看一下 Promise 的基本代码使用。

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

构造函数

//2.1 promise的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function MyPromise(executor) {
  let that = this;
  that.status = PENDING; //初始化状态
  that.value = null; //初始化value
  that.error = null; //初始化error
}

resolve/reject 实现

根据规范,resolve 方法是要把状态转为 fulfilled,reject 方法是要把状态转为 rejected

//这两个函数直接在构造函数中实现
function MyPromise(executor) {
  //...代码省略
  const that = this; //便于函数中访问

  //resolve
  function resolve(value) {
    if (that.status === PENDING) {
      that.status = FULFILLED;
      that.value = value;
    }
  }

  //reject
  function reject(error) {
    if (that.status === PENDING) {
      that.status = REJECTED;
      that.error = error;
    }
  }

  //将resolve和reject作为参数调用传进来的参数,记得加上try,如果捕获到错误就reject
  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

then 方法实现

在 Promise 的使用中,then 采用的是链式调用,promise.then(onFulfilled,onRejected),基础的框架如下:

MyPromise.prototype.then = function (onFulfilled, onRejected) {};

基于规范中的 2.2.1 中,onFulfilled/onRejected 都是可选参数,并且需要先检查是否为函数,不是的话就忽略。这个忽略不是说什么都不处理,而是将 value/error 直接处理,以至于可以做到参数继续传递。

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  onFulfilled =
    typeof onFulfilled === "function" ? onFulfilled : () => that.value;
  onFulfilled =
    typeof onRejected === "function"
      ? onRejected
      : () => {
          throw that.error;
        };
};

在 Promise 使用时,如果 promise 操作成功,就会调用 then 方法中的 onFulfilled,失败则会调用 onRejected,所以应当对应检查一下当前 status,然后进行调用。

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  //...省略
  if (this.status === FULFILLED) {
    onFulfilled(this.value);
  }
  if (this.status === REJECTED) {
    onFulfilled(this.value);
  }
};

在执行时,可能 executor 执行函数中的异步操作还没有结束,可能 status 还是’pending’状态,所以不能去执行 onFulfilled/onRejected 函数,所以需要对’pending’状态进行处理。并且 then 的链式调用可能有多个 onFulfilled/onRejected,所以需要数据将存储起来,等到 resolve/reject 之后一并执行。

首先更新构造函数里面的代码

function MyPromise(executor) {
  //...省略
  //构造函数里面添加两个数组存储成功和失败的回调
  that.onFulfilledCallback = [];
  that.onRejectedCallback = [];

  function resolve(value) {
    if (that.status === PENDING) {
      //...省略
      that.onFulfilledCallback.forEach((cb) => cb(that.value));
    }
  }

  function reject(error) {
    if (that.status === PENDING) {
      //...省略
      that.onRejectedCallback.forEach((cb) => cb(that.error));
    }
  }
}

更新 then 方法里面的代码

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  //...省略
  if (that.status === PENDING) {
    that.onFulfilledCallback.push(onFulfilled);
    that.onRejectedCallback.push(onRejected);
  }
};

根据规范 2.2.7,then 必须返回一个 promise,然后还规定了不同情况相应的处理方式。如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程(resolvePromise)将在后面实现。

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  let promise2 = null;
  //...省略
  if (that.status === FULFILLED) {
    return (promise2 = new Promise((resolve, reject) => {
      try {
        let x = onFulfilled(that.value);
        resolvePromise(promise2, x, resolve, reject);
      } catch (error) {
        reject(error);
      }
    }));
  }
  if (that.status === REJECTED) {
    return (promise2 = new Promise((resolve, reject) => {
      try {
        let x = onRejected(that.error);
        resolvePromise(promise2, x, resolve, reject);
      } catch (error) {
        reject(error);
      }
    }));
  }
  if (that.status === PENDING) {
    return (promise2 = new Promise((resolve, reject) => {
      that.onFulfilledCallback.push((value) => {
        try {
          let x = onFulfilled(value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
      that.onRejectedCallback.push((error) => {
        try {
          let x = onReject(error);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    }));
  }
};

resolvePromise

在 2.2.7.1 中提到如果 onFulfilled 或 onRejected 返回一个值 x, 运行 Promise Resolution Procedure Resolve(promise2, x)。所以现在来完成最复杂的 resolvePromise。

/**
 * 对resolve 进行改造增强 针对resolve中不同值情况 进行处理
 * @param  {promise} promise2 promise1.then方法返回的新的promise对象
 * @param  {[type]} x         promise1中onFulfilled的返回值
 * @param  {[type]} resolve   promise2的resolve方法
 * @param  {[type]} reject    promise2的reject方法
 */
function resolvePromise(promise, x, resolve, reject) {
  //2.3.1 promise和x引用同一个对象,则用TypeError作为原因拒绝(reject)
  if (promise === x) {
    reject(new TypeError());
  }
  //2.3.2 x为Promise
  else if (x instanceof MyPromise) {
    //2.3.2.1 x的status为pending,promise只需要保持为pending,直到x的状态为成功态或者失败态
    if (x.status === PENDING) {
      x.then(
        (res) => resolvePromise(promise, res, resolve, reject),
        (error) => reject(error)
      );
    }
    // x 已经处于执行态/拒绝态(值已经被解析为普通值),用相同的值执行传递下去 promise
    else {
      x.then(resolve, reject);
    }
  }
  //2.3.3   x是一个对象或者函数
  else if ((x !== null && typeof x === "object") || typeof x === "function") {
    let called = false;
    try {
      const then = x.then; //获取then函数
      //2.3.3.3 then是函数
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      }
      //2.3.3.4 then不是函数,resolve
      else {
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  }
  // 2.3.4 x不是对象或函数
  else {
    resolve(x);
  }
}

此时,我们已经将整体的 promise 实现完成,但是依旧有一个小点需要改进,基于 2.2.4 在执行上下文堆栈(execution context)仅包含平台代码之前,不得调用 onFulfilled 和 onRejected。简单来说,要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行,所以我们将 onFulfilled/onRejcted 方法放入 setTimeout 中。
更新 then 方法的代码如下:

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  //省略
  if (that.status === FULFILLED) {
    return (promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          let x = onFulfilled(that.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    }));
  }

  //失败态
  if (that.status === REJECTED) {
    return (promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          let x = onRejected(that.error);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    }));
  }
  //省略
};

代码测试

以上已经将 Promise 实现完成,Promise A+规范有一个标准的测试工具promises-aplus-tests,接下来对实现的 MyPromise 进行测试。首先需要将 promises-aplus-tests 安装到本地(这里暂不复述)。然后需要定义一个静态方法 deferred,官方的定义为:

deferred: 返回一个包含{ promise, resolve, reject }的对象
​promise 是一个处于pending状态的promise
​resolve(value) 用value解决上面那个promise
​reject(reason) 用reason拒绝上面那个promise

代码实现:

MyPromise.deferred = function () {
  const defer = {};
  defer.promise = new MyPromise((resolve, reject) => {
    defer.resolve = resolve;
    defer.reject = reject;
  });
  return defer;
};

try {
  module.exports = MyPromise;
} catch (e) {}

总结

跟着上文实现的 MyPromise 已经通过了所有的测试用例,点击查看详细代码
经过这个实现,对 Promsie 的原理有了更加深入的了解,但是还有一些 Promise 的 API 未能实现,会在接下来的时间中逐步更新。

打赏
支付宝
微信
本文作者:FBB
版权声明:本文首发于FBB的博客,转载请注明出处!