你不知道的Promise源码分析

最近在掘金上刷面试题的时候不可或非的多次遇到了 Promise 的问题,于是通过万能的搜索引擎,研究一番之后终于对 Promise 的源码才有了一个初步的理解,希望小伙伴能在此得到一点收获

你真的懂 promise 的运行机制吗?

new 一个 promise 的时候需要传递一个执行器函数,立即执行
执行器函数只接受两个参数 resolve reject
Promise 有三个状态 pedding fulfilled rejected

状态改变只能由 pendding -> fulfilled 或者 pendding -> rejected
状态一经确定就无法改变

我们所了解的 then 链式调用

Promise 的 then 可以链式调用(返回一个 promise 对象)
可接收两个参数(可缺省),一个是成功的回调 onFulfilled 一个是失败的回调 onRejected

如果成功且有返回值,则如果返回值为 Promise 对象则等待 promise 执行完成
如果成功则走下一个 Promise 成功的回调否则就走失败的回调

如果返回值是其他类型则直接作为下一个 then 对应的成功或失败的回调(在成功的回调返回就走下一个 then 成功的回调,反之亦然)

源码解读

const PENDDING = "pendding";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(fn) {
  let self = this;
  /**初始化状态*/
  self.status = PENDDING;

  /**保存成功回调函数*/
  self.onFulfilledCallBack = [];
  /**保存失败的回到数组*/
  self.onRejectedCallBack = [];

  /**执行成功回调 */
  function resolve(value) {
    if (self.status === PENDDING) {
      self.status = FULFILLED;
      self.value = value;
      self.onFulfilledCallBack.forEach(fn => fn(self.value));
    }
  }
  /**失败回调*/
  function reject(reason) {
    if (self.status === PENDDING) {
      self.status = REJECTED;
      self.reason = reason;
      self.onRejectedCallBack.forEach(fn => fn(self.reason));
    }
  }

  /**立即执行传入的函数,并加一层捕获异常 */
  try {
    fn(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
  /**确保用户在没有传入回调函数的时候将值往后传*/
  onFulfilled =
    typeof onFulfilled === "function" ? onFulfilled : value => value;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : reason => {
          throw reason;
        };
  let self = this;
  let promise2 = new Promise(function(resolve, reject) {
    if (self.status === FULFILLED) {
      try {
        /**因为Promise是属于微任务,所以在这里需要在外层包裹一个setTimeout
        保证它处于异步队列(关于这个问题的具体原因可以百度一下js事件循环机制)
        */
        setTimeout(() => {
          let x = onFulfilled(self.value);
          resolvePromise(promise2, x, resolve, reject);
        });
      } catch (error) {
        reject(error);
      }
    } else if (self.status === REJECTED) {
      try {
        setTimeout(() => {
          let x = onRejected(self.reason);
          resolvePromise(promise2, x, resolve, reject);
        });
      } catch (error) {
        reject(error);
      }
    } else if (self.status === PENDDING) {
      /**
    如果在调用then函数的时候promise状态还处于pendding状态
    则将处理函数分别存入对应的成功或失败的处理函数数组中
    */

      self.onFulfilledCallBack.push(() =>
        setTimeout(() => {
          try {
            let x = onFulfilled(self.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        })
      );

      self.onRejectedCallBack.push(() =>
        setTimeout(() => {
          try {
            let x = onRejected(self.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        })
      );
    }
  });
  return promise2;
};

/**处理在then函数中返回值*/
function resolvePromise(promise2, x, resolve, reject) {
  /**确保只执行一次*/
  let used;

  /**如果出现传入的x==promise2
  则会出现死循环,为了为了避免这种情况应该在这里加一层判断
  */
  if (x == promise2) {
    reject(new TypeError("Chaining cycle"));
  } else if ((x && typeof x === "function") || typeof x === "object") {
    try {
      let then = x.then;

      /**在这里加上一层判断是为了避免用户传入像{then:""}的对象*/
      if (typeof then === "function") {
        then.call(
          x,
          y => {
            if (used) return;
            used = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          e => {
            if (used) return;
            used = true;
            reject(e);
          }
        );
      } else {
        if (used) return;
        used = true;
        resolve(x);
      }
    } catch (error) {
      reject(error);
    }
  } else {
    x && resolve(x);
  }
}
module.exports = Promise;

可以参考这位大佬的文章
小邵教你玩转 promise


   转载规则


《你不知道的Promise源码分析》 Super man 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
nextTick nextTick
前言 众所周知,随着 Vue 技术的越来越热,大量的前端开发者开始探究这门神奇的框架,笔者也是从 JQuery 时代一脚迈进了 Vue 的世界。谈到Vue,在这呢,就不得不提一下笔者在研究一个Vue项目的时候碰到的问题,父组件修改标志位变
本篇 
你不知道的Promise源码分析 你不知道的Promise源码分析
最近在掘金上刷面试题的时候不可或非的多次遇到了 Promise 的问题,于是通过万能的搜索引擎,研究一番之后终于对 Promise 的源码才有了一个初步的理解,希望小伙伴能在此得到一点收获 你真的懂 promise 的运行机制吗? ne
  目录