JavaScript 实现Promise 及相关API


/**
 * 1. new Promise时,需要传递一个 executor 执行器,执行器立刻执行
 * 2. executor 接受两个参数,分别是 resolve 和 reject, resolve 和 reject 是 Promise 构造函数的内置函数。
 * 3. promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled
 * 4. promise 的状态一旦确认,就不会再改变
 * 5. promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled,
 *    和 promise 失败的回调 onRejected
 * 6. 如果调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值作为参数传递进去。
 *    如果promise已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。
 *    如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)
 * 7. then 的参数 onFulfilled 和 onRejected 可以缺省
 * 8. promise 可以then多次,promise 的then 方法返回一个 promise
 * 9. 如果 then 返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)
 * 10. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)
 * 11.如果 then 返回的是一个promise,那么会等这个promise执行完,promise如果成功,
 *    就走下一个then的成功,如果失败,就走下一个then的失败
 */

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function Promise(executor) {
  let self = this;
  self.status = PENDING; // 存储 Promise 的状态
  self.value = undefined; // 存储executor函数中业务代码执行成功的结果
  self.reason = undefined; // 存储executor函数中业务代码执行失败的原因
  self.resolvedCallback = []; // executor函数中业务代码执行成功回调函数的集合
  self.rejectedCallback = []; //executor函数中业务代码执行失败回调函数的集合

  function resolve(value) {
    // 只有当状态为 pending 才会改变,来保证一旦状态改变就不会再变。
    if (self.status === PENDING) {
      self.value = value;
      self.status = FULFILLED;
      // 依次调用成功回调函数
      self.resolvedCallback.forEach((callback) => callback());
    }
  }

  function reject(reason) {
    // 只有当状态为 pending 才会改变,来保证一旦状态改变就不会再变。
    if (self.status === PENDING) {
      self.reason = reason;
      self.status = REJECTED;
      // 依次调用失败回调函数
      self.rejectedCallback.forEach((callback) => callback());
    }
  }

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

/**
 * then
 * @param {*} onFulfilled
 * @param {*} onRejected
 * then 实例方法的业务用途应该是用来添加 Promise 状态改变时的回调函数,
 * 状态变为已成功的回调函数通过第一个参数传递进去添加,状态变为已失败的回调函数通过第二个参数传递进去添加。
 * 由于原生的 Promise 是V8引擎提供的微任务,我们无法还原V8引擎的实现,所以这里使用 setTimeout 模拟异步,所以原生的是微任务,这里是宏任务。
 * 实例方法 then 链式调用有两个要求:
 * 1. 在实例方法 then 后面可以直接使用实例方法 then。
 * 2. 在前面一个实例方法 then 返回一个值,不管是什么值,在后面一个实例方法 then 中都能获取到。
 */
Promise.prototype.then = function (onFulfilled, onRejected) {
  onFulfilled =
    typeof onFulfilled === "function" ? onFulfilled : (value) => value;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : (reason) => {
          throw reason;
        };
  let that = this;
  let promise2 = new Promise((resolve, reject) => {
    if (that.status === PENDING) {
      that.resolvedCallback.push(() => {
        setTimeout(() => {
          try {
            let x = onFulfilled(that.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      });
      that.rejectedCallback.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(that.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      });
    } else if (that.status === FULFILLED) {
      setTimeout(() => {
        try {
          let x = onFulfilled(that.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    } else if (that.status === REJECTED) {
      setTimeout(() => {
        try {
          let x = onRejected(that.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    }
  });
  // then必须返回一个promise
  return promise2;
};

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    reject(new TypeError("Promise Cycle Reference"));
  }
  if ((x && typeof x === "object") || typeof x === "function") {
    let called = false; // 确保只能调用一次
    try {
      // 防止重复去读取x.then,
      let then = x.then;
      // 为了让 Promise 更具有通用性,所以一个 thenable 对象也可以看做是一个 Promise 。
      // thenable 对象就是一个拥有 then 方法的对象, 如果then是函数就认为他是promise
      // thenable 对象可能不是通过 Promise 类 new 出来的,故不能通过 x instanceof Promise 来判断是不是一个 Promise
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            //如果Y是promise就继续递归promise
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        if (called) return;
        called = true;
        // 如果x是个普通对象,直接调用resolve(x)
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    // 如果x是个原始值,直接调用resolve(x),作为下个then成功的参数
    resolve(x);
  }
}

Promise.defer = Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};

/**
 * Promise.prototype.catch
 * @param {*} onRejected
 * 用于指定出错时的回调,是特殊的then方法,catch之后,可以继续 .then
 */
Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
};

/**
 * Promise.prototype.finally
 * @param {*} callback
 * 不管成功还是失败,都会走到finally中,并且finally之后,还可以继续then。并且会将值原封不动的传递给后面的then.
 */
Promise.prototype.finally = function (callback) {
  return this.then(
    (value) => {
      return Promise.resolve(callback()).then(() => {
        return value;
      });
    },
    (err) => {
      return Promise.reject(callback()).then(() => {
        throw err;
      });
    }
  );
};

/**
 * Promise.resolve
 * @param {*} param
 * 1. 如果 value 是个 thenable 对象,返回的promise会“跟随”这个thenable的对象,采用它的最终状态
 * 2. 如果传入的value本身就是promise对象,那么Promise.resolve将不做任何修改、原封不动地返回这个promise对象。
 * 3. 其他情况,直接返回以该值为成功状态的promise对象。
 */
Promise.resolve = function (param) {
  if (param instanceof Promise) {
    return param;
  }
  return new Promise((resolve, reject) => {
    if (
      param &&
      typeof param === "object" &&
      typeof param.then === "function"
    ) {
      setTimeout(() => {
        param.then(resolve, reject);
      });
    } else {
      resolve(param);
    }
  });
};

/**
 * Promise.reject
 * @param {*} reason
 * Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数
 */
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason);
  });
};

/**
 * Promise.all
 * @param {*} promises
 * 1. 如果传入的参数是一个空的可迭代对象,那么此promise对象回调完成(resolve),只有此情况,是同步执行的,其它都是异步返回的。
 * 2. 如果传入的参数不包含任何 promise,则返回一个异步完成.
 * 3. promises 中所有的promise都“完成”时或参数中不包含 promise 时回调完成。
 * 4. 如果参数中有一个promise失败,那么Promise.all返回的promise对象失败
 * 5. 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组
 */
Promise.all = function (promises) {
  promises = Array.from(promises);
  return new Promise((resolve, reject) => {
    let index = 0,
      result = [];
    if (promises.length === 0) {
      resolve(result);
    } else {
      for (let i = 0; i < promises.length; i++) {
        Promise.resolve(promises[i]).then(
          (data) => {
            result[i] = data;
            if (++index === promises.length) {
              resolve(result);
            }
          },
          (err) => {
            reject(err);
            return;
          }
        );
      }
    }
  });
};

/**
 * Promise.race
 * @param {*} promises
 * Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝
 */
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (const promise of promises) {
      Promise.resolve(promise).then(resolve, reject);
    }
  });
};

/**
 * Promise.allSettled
 * @param {*} promises
 * 返回一个promise,该promise在所有给定的promise已被解析或被拒绝后解析,并且每个对象都描述每个promise的结果。
 */
Promise.allSettled = function (promises) {
  if (promises instanceof Array) {
    let result = [];
    promises.forEach((promise) => {
      result.push(
        new Promise((resolve, reject) => {
          promise
            .then((value) => {
              resolve({ state: true, data: value });
            })
            .catch((e) => {
              resolve({ state: false, data: e });
            });
        })
      );
    });
    return Promise.all(result);
  }
  return Promise.reject();
};

module.exports = Promise;

 上一篇
JavaScript 中的继承 JavaScript 中的继承
JavaScript 中的继承1. 原型链继承原型链的问题: 原型中包含的引用值会在所有实例间共享 子类型在实例化时不能给父类构造函数传参 function SuperType() { this.property = true;
下一篇 
节流和防抖 节流和防抖
节流和防抖防抖防抖的原理就是:你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行。
  目录