乐趣区

关于javascript:实现一个-PromisesA

残缺高频题库仓库地址:https://github.com/hzfe/aweso…

残缺高频题库浏览地址:https://febook.hzfe.org/

这是一道有着成熟的业界标准的 coding 题,实现这道题的前置常识就是要理解什么是 Promises/A+。

这道题的难点就在于它是有标准的,任何一个不满足所有标准条件的解答都是谬误的。同时,成熟的标准也配套了成熟的测试用例,官网提供了 872 个测试用例针对标准中的所有条件一一进行检测,哪怕只有一条失败,那也是谬误的解答。

而这道题的答题要害也恰好是因为它是有标准的,只有咱们对于标准了然于胸,那么编写代码天然也是瓜熟蒂落。因为官网标准提供了一个合乎 Promises/A+ 标准的 Promise 应该具备的全副条件,并且在 Requirements 一节中构造清晰、逻辑充沛的表述了进去,咱们只需将标准中的文字转变为代码,就可能实现一个 Promises/A+ 标准的 Promise。

编写代码

因为标准条例较多,咱们拆解成三块来了解记忆,别离是:根底框架、then 办法和 Promise 处理程序。

每一块由两局部形成:

  • 流程图:展现了代码逻辑的关键步骤,也是优先须要了解记忆的点。
  • 实现代码:展现了代码逻辑的具体细节,是对关键步骤的欠缺补全。

其中, 波及到标准条例的点会注明标准序号

再次强调, 本题的答题要害是相熟标准 !磨刀不误砍柴工,务必先相熟!相熟!相熟!

1. 根底框架

1.1 流程图

1.2 实现代码

function Promise(executor) {
  // 2.1. Promise 的状态
  // Promise 必须处于以下三种状态之一:pending,fulfilled 或者 rejected。this.state = "pending";
  // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须依照它们对应的 then 的原始调用程序来执行。this.onFulfilledCallback = [];
  // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须依照它们对应的 then 的原始调用程序来执行。this.onRejectedCallback = [];

  const self = this;

  function resolve(value) {setTimeout(function () {
      // 2.1.1. 当 Promise 处于 pending 状态时:// 2.1.1.1. 能够转换到 fulfilled 或 rejected 状态。// 2.1.2. 当 Promise 处于 fulfilled 状态时:// 2.1.2.1. 不得过渡到任何其余状态。// 2.1.2.2. 必须有一个不能扭转的值。if (self.state === "pending") {
        self.state = "fulfilled";
        self.data = value;
        // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须依照它们对应的 then 的原始调用程序来执行。for (let i = 0; i < self.onFulfilledCallback.length; i++) {self.onFulfilledCallback[i](value);
        }
      }
    });
  }

  function reject(reason) {setTimeout(function () {
      // 2.1.1. 当 Promise 处于 pending 状态时:// 2.1.1.1. 能够转换到 fulfilled 或 rejected 状态。// 2.1.3. 当 Promise 处于 rejected 状态时:// 2.1.2.1. 不得过渡到任何其余状态。// 2.1.2.2. 必须有一个不能扭转的值。if (self.state === "pending") {
        self.state = "rejected";
        self.data = reason;
        // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须依照它们对应的 then 的原始调用程序来执行。for (let i = 0; i < self.onRejectedCallback.length; i++) {self.onRejectedCallback[i](reason);
        }
      }
    });
  }

  // 补充阐明:用户传入的函数可能也会执行异样,所以这里用 try...catch 包裹
  try {executor(resolve, reject);
  } catch (reason) {reject(reason);
  }
}

2. then 办法

2.1 流程图

2.2 实现代码

// 2.2. then 办法
// 一个 promise 必须提供一个 then 办法来拜访其以后值或最终值或 rejected 的起因。// 一个 promise 的 then 办法承受两个参数:// promise.then(onFulfilled, onRejected)
Promise.prototype.then = function (onFulfilled, onRejected) {
  const self = this;

  let promise2;
  // 2.2.7. then 必须返回一个 promise
  return (promise2 = new Promise(function (resolve, reject) {
    // 2.2.2. 如果 onFulfilled 是一个函数:
    // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。// 2.2.2.2. 它肯定不能在 promise 的状态变为 fulfilled 前被调用。// 2.2.2.3. 它最多只能被调用一次。if (self.state === "fulfilled") {
      // 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅蕴含平台代码之前不得调用。// 3.1. 这能够通过“宏工作”机制(例如 setTimeout 或 setImmediate)或“微工作”机制(例如 MutationObserver 或 process.nextTick)来实现。setTimeout(function () {
        // 2.2.1. onFulfilled 和 onRejected 都是可选参数:// 2.2.1.1. 如果 onFulfilled 不是一个函数,它必须被疏忽。if (typeof onFulfilled === "function") {
          try {
            // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。const x = onFulfilled(self.data);
            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {
            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异样,promise2 必须用 e 作为 reason 来变为 rejected 状态。reject(e);
          }
        } else {
          // 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。resolve(self.data);
        }
      });
    }
    // 2.2.3. 如果 onRejected 是一个函数,// 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。// 2.2.3.2. 它肯定不能在 promise 的状态变为 rejected 前被调用。// 2.2.3.3. 它最多只能被调用一次。else if (self.state === "rejected") {
      // 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅蕴含平台代码之前不得调用。// 3.1. 这能够通过“宏工作”机制(例如 setTimeout 或 setImmediate)或“微工作”机制(例如 MutationObserver 或 process.nextTick)来实现。setTimeout(function () {
        // 2.2.1. onFulfilled 和 onRejected 都是可选参数:// 2.2.1.2. 如果 onRejected 不是一个函数,它必须被疏忽。if (typeof onRejected === "function") {
          try {
            // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。const x = onRejected(self.data);
            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {
            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异样,promise2 必须用 e 作为 reason 来变为 rejected 状态。reject(e);
          }
        }
        // 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。else {reject(self.data);
        }
      });
    } else if (self.state === "pending") {
      // 2.2.6. then 可能会被同一个 promise 屡次调用。// 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须依照它们对应的 then 的原始调用程序来执行。self.onFulfilledCallback.push(function (promise1Value) {if (typeof onFulfilled === "function") {
          try {
            // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。const x = onFulfilled(self.data);
            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {
            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异样,promise2 必须用 e 作为 reason 来变为 rejected 状态。reject(e);
          }
        }
        // 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。else {resolve(promise1Value);
        }
      });
      // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须依照它们对应的 then 的原始调用程序来执行。self.onRejectedCallback.push(function (promise1Reason) {if (typeof onRejected === "function") {
          try {
            // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。const x = onRejected(self.data);
            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {
            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异样,promise2 必须用 e 作为 reason 来变为 rejected 状态。reject(e);
          }
        }
        // 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。else {reject(promise1Reason);
        }
      });
    }
  }));
};

3. Promise 处理程序

3.1 流程图

3.2 实现代码

// 2.3. Promise 处理程序
// Promise 处理程序是一个将 promise 和 value 作为输出的形象操作,咱们将其示意为 [[Resolve]](promise, x)。// 补充阐明:这里咱们将 resolve 和 reject 也传入进来,因为后续要依据不同的逻辑对 promise 执行 fulfill 或 reject 操作。function promiseResolutionProcedure(promise2, x, resolve, reject) {
  // 2.3.1. 如果 promise 和 x 援用的是同一个对象,promise 将以一个 TypeError 作为 reason 来进行 reject。if (promise2 === x) {return reject(new TypeError("Chaining cycle detected for promise"));
  }

  // 2.3.2. 如果 x 是一个 promise,依据它的状态:if (x instanceof Promise) {
    // 2.3.2.1. 如果 x 的状态为 pending,promise 必须放弃 pending 状态直到 x 的状态变为 fulfilled 或 rejected。if (x.state === "pending") {x.then(function (value) {promiseResolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    }
    // 2.3.2.2. 如果 x 的状态为 fulfilled,那么 promise 也用同样的值来执行 fulfill 操作。else if (x.state === "fulfilled") {resolve(x.data);
    }
    // 2.3.2.3. 如果 x 的状态为 rejected,那么 promise 也用同样的 reason 来执行 reject 操作。else if (x.state === "rejected") {reject(x.data);
    }
    return;
  }

  // 2.3.3. 除此之外,如果 x 是一个对象或者函数,if (x && (typeof x === "object" || typeof x === "function")) {
    // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者屡次调用同样的参数,则第一次调用优先,任何之后的调用都将被疏忽。let isCalled = false;

    try {
      // 2.3.3.1. 申明一个 then 变量来保留 then
      let then = x.then;
      // 2.3.3.3. 如果 then 是一个函数,将 x 作为 this 来调用它,第一个参数为 resolvePromise,第二个参数为 rejectPromise,其中:if (typeof then === "function") {
        then.call(
          x,
          // 2.3.3.3.1. 假如 resolvePromise 应用一个名为 y 的值来调用,运行 promise 处理程序 [[Resolve]](promise, y)。function resolvePromise(y) {
            // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者屡次调用同样的参数,则第一次调用优先,任何之后的调用都将被疏忽。if (isCalled) return;
            isCalled = true;
            return promiseResolutionProcedure(promise2, y, resolve, reject);
          },
          // 2.3.3.3.2. 假如 rejectPromise 应用一个名为 r 的 reason 来调用,则用 r 作为 reason 对 promise 执行 reject 操作。function rejectPromise(r) {
            // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者屡次调用同样的参数,则第一次调用优先,任何之后的调用都将被疏忽。if (isCalled) return;
            isCalled = true;
            return reject(r);
          }
        );
      }
      // 2.3.3.4. 如果 then 不是一个函数,应用 x 作为值对 promise 执行 fulfill 操作。else {resolve(x);
      }
    } catch (e) {
      // 2.3.3.2. 如果检索 x.then 的后果抛出异样 e,应用 e 作为 reason 对 promise 执行 reject 操作。// 2.3.3.3.4. 如果调用 then 时抛出一个异样 e,// 2.3.3.3.4.1. 如果 resolvePromise 或 rejectPromise 曾经被调用过了,则疏忽异样。if (isCalled) return;
      isCalled = true;
      // 2.3.3.3.4.2. 否则,应用 e 作为 reason 对 promise 执行 reject 操作。reject(e);
    }
  }
  // 2.3.4. 如果 x 不是一个对象或者函数,应用 x 作为值对 promise 执行 fulfill 操作。else {resolve(x);
  }
}

4. 残缺代码

function Promise(executor) {
  this.state = "pending";
  this.onFulfilledCallback = [];
  this.onRejectedCallback = [];

  const self = this;

  function resolve(value) {setTimeout(function () {if (self.state === "pending") {
        self.state = "fulfilled";
        self.data = value;
        for (let i = 0; i < self.onFulfilledCallback.length; i++) {self.onFulfilledCallback[i](value);
        }
      }
    });
  }

  function reject(reason) {setTimeout(function () {if (self.state === "pending") {
        self.state = "rejected";
        self.data = reason;
        for (let i = 0; i < self.onRejectedCallback.length; i++) {self.onRejectedCallback[i](reason);
        }
      }
    });
  }

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

Promise.prototype.then = function (onFulfilled, onRejected) {
  const self = this;

  let promise2;

  return (promise2 = new Promise(function (resolve, reject) {if (self.state === "fulfilled") {setTimeout(function () {if (typeof onFulfilled === "function") {
          try {const x = onFulfilled(self.data);

            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {reject(e);
          }
        } else {resolve(self.data);
        }
      });
    } else if (self.state === "rejected") {setTimeout(function () {if (typeof onRejected === "function") {
          try {const x = onRejected(self.data);

            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {reject(e);
          }
        } else {reject(self.data);
        }
      });
    } else if (self.state === "pending") {self.onFulfilledCallback.push(function (promise1Value) {if (typeof onFulfilled === "function") {
          try {const x = onFulfilled(self.data);

            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {reject(e);
          }
        } else {resolve(promise1Value);
        }
      });

      self.onRejectedCallback.push(function (promise1Reason) {if (typeof onRejected === "function") {
          try {const x = onRejected(self.data);

            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {reject(e);
          }
        } else {reject(promise1Reason);
        }
      });
    }
  }));
};

function promiseResolutionProcedure(promise2, x, resolve, reject) {if (promise2 === x) {return reject(new TypeError("Chaining cycle detected for promise"));
  }

  if (x instanceof Promise) {if (x.state === "pending") {x.then(function (value) {promiseResolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else if (x.state === "fulfilled") {resolve(x.data);
    } else if (x.state === "rejected") {reject(x.data);
    }
    return;
  }

  if (x && (typeof x === "object" || typeof x === "function")) {
    let isCalled = false;

    try {
      let then = x.then;

      if (typeof then === "function") {
        then.call(
          x,
          function resolvePromise(y) {if (isCalled) return;
            isCalled = true;
            return promiseResolutionProcedure(promise2, y, resolve, reject);
          },
          function rejectPromise(r) {if (isCalled) return;
            isCalled = true;
            return reject(r);
          }
        );
      } else {resolve(x);
      }
    } catch (e) {if (isCalled) return;
      isCalled = true;
      reject(e);
    }
  } else {resolve(x);
  }
}

module.exports = Promise;

测试代码

结尾咱们就说过,Promises/A+ 标准配套了成熟的测试用例,咱们必须全副通过才算代码编写正确。上面咱们就用 872 个官网测试用例来测试一下咱们的残缺代码是否合乎 Promises/A+ 标准。

1. 裸露一个简略的适配器接口

// test.js

// 导入咱们写好的 promise
const Promise = require("./promise.js");

// 依据官网文档裸露一个 deferred 办法,返回一个蕴含 promise、resolve、reject 的对象
Promise.deferred = function () {const obj = {};

  obj.promise = new Promise(function (resolve, reject) {
    obj.resolve = resolve;
    obj.reject = reject;
  });

  return obj;
};

module.exports = Promise;

2. 运行命令

$ npx promises-aplus-tests test.js

3. 测试后果

完满通过!

参考资料

  1. Promises/A+
  2. Promises/A+ Compliance Test Suite
退出移动版