返回

手写Promise,跑Promise A+规范

前端

前言

在异步编程中,Promise是一种非常有用的工具,它可以帮助我们处理异步操作,并使代码更加简洁和可读。Promise A+规范定义了Promise对象的行为和用法,它被广泛地应用于JavaScript社区,并成为了JavaScript异步编程的标准。

Promise的基本概念

Promise是一个对象,它表示一个异步操作的最终完成或失败的状态。Promise有三种状态:pending、fulfilled和rejected。

  • pending :表示异步操作尚未完成。
  • fulfilled :表示异步操作已成功完成。
  • rejected :表示异步操作已失败。

Promise对象可以通过then方法来注册回调函数,当Promise的状态发生改变时,相应的回调函数就会被调用。then方法有两个参数:onFulfilled和onRejected。onFulfilled在Promise的状态变为fulfilled时被调用,onRejected在Promise的状态变为rejected时被调用。

手写Promise

现在,我们来一步一步地手写一个Promise。

function Promise(executor) {
  this.state = 'pending';
  this.value = undefined;
  this.reason = undefined;
  this.onFulfilledCallbacks = [];
  this.onRejectedCallbacks = [];

  const resolve = (value) => {
    if (this.state !== 'pending') {
      return;
    }

    this.state = 'fulfilled';
    this.value = value;

    this.onFulfilledCallbacks.forEach((callback) => {
      callback(this.value);
    });
  };

  const reject = (reason) => {
    if (this.state !== 'pending') {
      return;
    }

    this.state = 'rejected';
    this.reason = reason;

    this.onRejectedCallbacks.forEach((callback) => {
      callback(this.reason);
    });
  };

  executor(resolve, reject);
}

在上面的代码中,我们定义了一个Promise类,并在构造函数中初始化了Promise的状态、值和原因,以及fulfilled和rejected回调函数的数组。然后,我们定义了resolve和reject方法,它们分别用于将Promise的状态改为fulfilled和rejected。

接下来,我们在Promise类中定义了then方法。

Promise.prototype.then = function (onFulfilled, onRejected) {
  if (typeof onFulfilled !== 'function') {
    onFulfilled = (value) => value;
  }

  if (typeof onRejected !== 'function') {
    onRejected = (reason) => {
      throw reason;
    };
  }

  const promise2 = new Promise((resolve, reject) => {
    if (this.state === 'fulfilled') {
      setTimeout(() => {
        try {
          const x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (err) {
          reject(err);
        }
      }, 0);
    } else if (this.state === 'rejected') {
      setTimeout(() => {
        try {
          const x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (err) {
          reject(err);
        }
      }, 0);
    } else {
      this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      });

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

  return promise2;
};

在上面的代码中,我们首先检查onFulfilled和onRejected是否为函数,如果不是,则将其替换为默认的函数。然后,我们创建一个新的Promise对象promise2,并在其构造函数中定义了resolve和reject方法。

如果当前Promise的状态是fulfilled,则我们使用setTimeout方法将onFulfilled回调函数推迟到下一个事件循环中执行。这确保了onFulfilled回调函数是在resolvePromise方法之后执行的,以防止出现同步错误。

如果当前Promise的状态是rejected,则我们使用setTimeout方法将onRejected回调函数推迟到下一个事件循环中执行。这确保了onRejected回调函数是在rejectPromise方法之后执行的,以防止出现同步错误。

如果当前Promise的状态是pending,则我们将onFulfilled和onRejected回调函数推入相应的数组中。当当前Promise的状态改变时,我们会调用这些回调函数。

最后,我们将promise2返回,以便可以将then方法链式调用。

测试Promise

现在,我们已经手写了一个Promise,我们可以使用单元测试来验证它的实现是否符合Promise A+规范。

const assert = require('assert');

describe('Promise', function () {
  it('should be a function', function () {
    assert.strictEqual(typeof Promise, 'function');
  });

  it('should have a then method', function () {
    const promise = new Promise(() => {});
    assert.strictEqual(typeof promise.then, 'function');
  });

  it('should call onFulfilled when the Promise is resolved', function (done) {
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success');
      }, 10);
    });

    promise.then(function (value) {
      assert.strictEqual(value, 'success');
      done();
    });
  });

  it('should call onRejected when the Promise is rejected', function (done) {
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('error');
      }, 10);
    });

    promise.then(null, function (reason) {
      assert.strictEqual(reason, 'error');
      done();
    });
  });

  it('should chain then calls', function (done) {
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success');
      }, 10);
    });

    promise.then(function (value) {
      return value + '!';
    }).then(function (value) {
      assert.strictEqual(value, 'success!');
      done();
    });
  });

  it('should handle errors in onFulfilled and onRejected', function (done) {
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success');
      }, 10);
    });

    promise.then(function () {
      throw new Error('error');
    }).then(null, function (reason) {
      assert.strictEqual(reason.message, 'error');
      done();
    });
  });

  it('should not call onFulfilled or onRejected more than once', function (done) {
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success');
      }, 10);
    });

    promise.then(function () {
      done();
    });

    promise.then(function () {
      assert.fail('onFulfilled should not be called more than once');
    });
  });
});

上面的单元测试涵盖了Promise A+规范中的大部分要求。您可以使用这些测试来验证您手写的Promise是否符合规范。

结语

本文带领您一步一步地手写了一个Promise,并根据Promise A+规范对其实施了测试。通过本教程,您应该对Promise及其底层机制有了更深入的理解。如果您对Promise有更多的疑问,可以查阅Promise A+规范,或在网上搜索相关资料。