阿西河

所有教程

公众号
🌙
阿西河前端的公众号

我的收藏

    最近访问  (文章)

      教程列表

      抓包专区
      测试专区

      Javascript 如何实现⼀个 Promise

      问题

      Javascript 如何实现⼀个 Promise

      答案

      Promise 是 ES6 新增的语法,解决了回调地狱的问题。

      可以把 Promise 看成⼀个状态机。初始是 pending 状态,可以通过函数 resolve 和 reject ,将状态转变为 resolved 或者 rejected 状态,状态⼀旦改变就不能再次变 化。

      then 函数会返回⼀个 Promise 实例,并且该返回值是⼀个新的实例⽽不是之前的实 例。因为 Promise 规范规定除了 pending 状态,其他状态是不可以改变的,如果返回 的是⼀个相同实例的话,多个 then 调⽤就失去意义了。 对于 then 来说,本质上可以 把它看成是 flatMap

      实现

      // 三种状态
      const PENDING = "pending";
      const RESOLVED = "resolved";
      const REJECTED = "rejected";
      // promise 接收⼀个函数参数,该函数会⽴即执⾏
      function MyPromise(fn) {
          let _this = this;
          _this.currentState = PENDING;
          _this.value = undefined;
          // ⽤于保存 then 中的回调,只有当 promise
          // 状态为 pending 时才会缓存,并且每个实例⾄多缓存⼀个
          _this.resolvedCallbacks = [];
          _this.rejectedCallbacks = [];
          _this.resolve = function (value) {
              if (value instanceof MyPromise) {
                  // 如果 value 是个 Promise,递归执⾏
                  return value.then(_this.resolve, _this.reject)
              }
              setTimeout(() => { // 异步执⾏,保证执⾏顺序
                  if (_this.currentState === PENDING) {
                      _this.currentState = RESOLVED;
                      _this.value = value;
                      _this.resolvedCallbacks.forEach(cb => cb());
                  }
              })
          };
          _this.reject = function (reason) {
              setTimeout(() => { // 异步执⾏,保证执⾏顺序
                  if (_this.currentState === PENDING) {
                      _this.currentState = REJECTED;
                      _this.value = reason;
                      _this.rejectedCallbacks.forEach(cb => cb());
                  }
              })
          }
          // ⽤于解决以下问题
          // new Promise(() => throw Error('error))
          try {
              fn(_this.resolve, _this.reject);
          } catch (e) {
              _this.reject(e);
          }
      }
      MyPromise.prototype.then = function (onResolved, onRejected) {
          var self = this;
          // 规范 2.2.7,then 必须返回⼀个新的 promise
          var promise2;
          // 规范 2.2.onResolved 和 onRejected 都为可选参数
          // 如果类型不是函数需要忽略,同时也实现了透传
          // Promise.resolve(4).then().then((value) => console.log(value))
          onResolved = typeof onResolved === 'function' ? onResolved : v => v;
          onRejected = typeof onRejected === 'function' ? onRejected : r => throw r
          if (self.currentState === RESOLVED) {
              return (promise2 = new MyPromise(function (resolve, reject) {
                  // 规范 2.2.4,保证 onFulfilled,onRjected 异步执⾏
                  // 所以⽤了 setTimeout 包裹下
                  setTimeout(function () {
                      try {
                          var x = onResolved(self.value);
                          resolutionProcedure(promise2, x, resolve, reject);
                      } catch (reason) {
                          reject(reason);
                      }
                  });
              }));
          }
          if (self.currentState === REJECTED) {
              return (promise2 = new MyPromise(function (resolve, reject) {
                  setTimeout(function () {
                      // 异步执⾏onRejected
                      try {
                          var x = onRejected(self.value);
                          resolutionProcedure(promise2, x, resolve, reject);
                      } catch (reason) {
                          reject(reason);
                      }
                  });
              }));
          }
          if (self.currentState === PENDING) {
              return (promise2 = new MyPromise(function (resolve, reject) {
                  self.resolvedCallbacks.push(function () {
                      // 考虑到可能会有报错,所以使⽤ try/catch 包裹
                      try {
                          var x = onResolved(self.value);
                          resolutionProcedure(promise2, x, resolve, reject);
                      } catch (r) {
                          reject(r);
                      }
                  });
                  self.rejectedCallbacks.push(function () {
                      try {
                          var x = onRejected(self.value);
                          resolutionProcedure(promise2, x, resolve, reject);
                      } catch (r) {
                          reject(r);
                      }
                  });
              }));
          }
      };
      // 规范 2.3
      function resolutionProcedure(promise2, x, resolve, reject) {
          // 规范 2.3.1,x 不能和 promise2 相同,避免循环引⽤
          if (promise2 === x) {
              return reject(new TypeError("Error"));
          }
          // 规范 2.3.2
          // 如果 x 为 Promise,状态为 pending 需要继续等待否则执⾏
          if (x instanceof MyPromise) {
              if (x.currentState === PENDING) {
                  x.then(function (value) {
                      // 再次调⽤该函数是为了确认 x resolve 的
                      // 参数是什么类型,如果是基本类型就再次 resolve
                      // 把值传给下个 then
                      resolutionProcedure(promise2, value, resolve, reject);
                  }, reject);
              } else {
                  x.then(resolve, reject);
              }
              return;
          }
          // 规范 2.3.3.3.3
          // reject 或者 resolve 其中⼀个执⾏过得话,忽略其他的
          let called = false;
          // 规范 2.3.3,判断 x 是否为对象或者函数
          if (x !== null && (typeof x === "object" || typeof x === "function")) {
              // 规范 2.3.3.2,如果不能取出 then,就 reject
              try {
                  // 规范 2.3.3.1
                  let then = x.then;
                  // 如果 then 是函数,调⽤ x.then
                  if (typeof then === "function") {
                      // 规范 2.3.3.3
                      then.call(
                          x,
                          y => {
                              if (called) return;
                              called = true;
                              // 规范 2.3.3.3.1
                              resolutionProcedure(promise2, y, resolve, reject);
                          },
                          e => {
                              if (called) return;
                              called = true;
                              reject(e);
                          }
                      );
                  } else {
                      // 规范 2.3.3.4
                      resolve(x);
                  }
              } catch (e) {
                  if (called) return;
                  called = true;
                  reject(e);
              }
          } else {
              // 规范 2.3.4,x 为基本类型
          resolve(x);
          }
      }
      
      

      更多面试题

      如果你想了解更多的前端面试题,可以查看本站的WEB前端面试题 ,这里基本包涵了市场上的所有前端方面的面试题,也有一些大公司的面试图,可以让你面试更加顺利。

      面试题
      HTMLCSSJavaScript
      jQueryVue.jsReact
      算法HTTPBabel
      BootStrapElectronGulp
      Node.js前端经验相关前端综合
      Webpack微信小程序-

      这些题库还在更新中,如果你有不错的面试题库欢迎分享给我,我整理后放上来;人人为我,我为人人,互帮互助,共同提高,祝大家都拿到心仪的Offer!

      目录
      目录