返回

重温ES6经典之模拟实现Promise

前端

前言

ES6的出现为JavaScript带来了众多激动人心的新特性,其中Promise作为异步编程的利器,以其简洁优雅的语法和强大的功能赢得了广大开发者的青睐。Promise的出现,为我们提供了更加高效、可读性更强的异步编程方式,使我们能够轻松处理复杂的异步操作。

Promise的模拟实现

为了更好地理解Promise的运作机制,我们不妨从头开始,模拟实现一个简单的Promise。

首先,我们需要定义一个Promise构造函数。Promise构造函数接受一个函数作为参数,该函数将作为Promise的执行器。执行器有两个参数,分别是resolve和reject,这两个函数用于分别将Promise的状态变为fulfilled和rejected。

function Promise(executor) {
  this.state = "pending"; // 初始状态为pending
  this.value = null; // 存储成功结果
  this.reason = null; // 存储失败结果
  this.onFulfilledCallbacks = []; // 存放成功的回调函数
  this.onRejectedCallbacks = []; // 存放失败的回调函数

  const resolve = (value) => {
    if (this.state === "pending") {
      this.state = "fulfilled";
      this.value = value;
      this.onFulfilledCallbacks.forEach((callback) => {
        callback(value);
      });
    }
  };

  const reject = (reason) => {
    if (this.state === "pending") {
      this.state = "rejected";
      this.reason = reason;
      this.onRejectedCallbacks.forEach((callback) => {
        callback(reason);
      });
    }
  };

  executor(resolve, reject);
}

Promise构造函数创建了一个新的Promise实例,并将其状态初始化为“pending”。当执行器调用resolve或reject时,Promise的状态将分别变为“fulfilled”或“rejected”。同时,根据Promise的状态,将成功的回调函数或失败的回调函数加入对应的回调函数数组中。

接下来,我们需要定义一个then方法,它将作为Promise实例的方法供外部调用。then方法接受两个参数,分别是成功的回调函数和失败的回调函数。当Promise的状态变为“fulfilled”时,将调用成功的回调函数,并将Promise的成功结果作为参数传递给该函数。当Promise的状态变为“rejected”时,将调用失败的回调函数,并将Promise的失败结果作为参数传递给该函数。

Promise.prototype.then = function (onFulfilled, onRejected) {
  return new Promise((resolve, reject) => {
    if (this.state === "fulfilled") {
      setTimeout(() => {
        try {
          const value = onFulfilled(this.value);
          resolve(value);
        } catch (error) {
          reject(error);
        }
      }, 0);
    } else if (this.state === "rejected") {
      setTimeout(() => {
        try {
          const reason = onRejected(this.reason);
          resolve(reason);
        } catch (error) {
          reject(error);
        }
      }, 0);
    } else {
      this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            const value = onFulfilled(this.value);
            resolve(value);
          } catch (error) {
            reject(error);
          }
        }, 0);
      });

      this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            const reason = onRejected(this.reason);
            resolve(reason);
          } catch (error) {
            reject(error);
          }
        }, 0);
      });
    }
  });
};

then方法返回一个新的Promise实例,该Promise实例的状态取决于传入的回调函数的返回值。如果成功的回调函数返回一个Promise,则新的Promise实例的状态取决于该Promise的状态。如果成功的回调函数返回一个普通值,则新的Promise实例的状态变为“fulfilled”,并将该普通值作为新的Promise实例的成功结果。如果失败的回调函数返回一个Promise,则新的Promise实例的状态取决于该Promise的状态。如果失败的回调函数返回一个普通值,则新的Promise实例的状态变为“rejected”,并将该普通值作为新的Promise实例的失败结果。

需要注意的是,由于JavaScript的异步特性,回调函数的执行顺序是无法确定的。为了避免出现执行顺序混乱的情况,我们使用setTimeout将回调函数的执行推迟到下一个事件循环中。这样,我们可以确保回调函数总是按照正确的顺序执行。

Promises/A+规范的实现

Promises/A+规范是Promise的官方规范,它定义了Promise必须遵守的标准。为了确保我们的Promise实现与Promises/A+规范兼容,我们需要对代码进行一些修改。

首先,我们需要定义一个新的PromiseResolver类,该类将作为Promise的执行器。PromiseResolver类包含两个属性,分别是promiseresolvepromise属性保存着当前的Promise实例,resolve属性保存着resolve函数。

class PromiseResolver {
  constructor() {
    this.promise = new Promise((resolve) => {
      this.resolve = resolve;
    });
  }
}

接下来,我们需要修改Promise构造函数,使其使用PromiseResolver类来创建Promise实例。

function Promise(executor) {
  const resolver = new PromiseResolver();

  const resolve = (value) => {
    resolver.resolve(value);
  };

  const reject = (reason) => {
    resolver.reject(reason);
  };

  executor(resolve, reject);

  return resolver.promise;
}

这样,我们就确保了Promise实例总是按照Promises/A+规范创建。

此外,我们需要对then方法进行一些修改,以使其符合Promises/A+规范。

Promise.prototype.then = function (onFulfilled, onRejected) {
  return this._then(onFulfilled, onRejected);
};

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

  return new Promise((resolve, reject) => {
    const fulfilledHandler = (value) => {
      try {
        const result = onFulfilled ? onFulfilled(value) : value;
        resolve(result);
      } catch (error) {
        reject(error);
      }
    };

    const rejectedHandler = (reason) => {
      try {
        const result = onRejected ? onRejected(reason) : reason;
        resolve(result);
      } catch (error) {
        reject(error);
      }
    };

    if (self.state === "fulfilled") {
      setTimeout(() => {
        fulfilledHandler(self.value);
      }, 0);
    } else if (self.state === "rejected") {
      setTimeout(() => {
        rejectedHandler(self.reason);
      }, 0);
    } else {
      self.onFulfilledCallbacks.push(fulfilledHandler);
      self.onRejectedCallbacks.push(rejectedHandler);
    }
  });
};

修改后的then方法与Promises/A+规范完全兼容,它可以正确处理各种情况,并确保回调函数总是按照正确的顺序执行。

结语

通过模拟实现Promise,我们不仅对Promise的运作机制有了更深入的理解,也对Promises/A+规范有了更加全面的认识。通过手动构建Promise,我们能够更加深刻地体会到Promise的强大之处以及异步编程的精髓。如果您想要在项目中使用Promise,建议您使用成熟的Promise库,如bluebirdqnative-promises,以避免出现兼容性问题和性能问题。