返回

从手写Promise,到通过Promises/A+全部测试

前端

一、写在开始前

手写Promise,是我作为一名前端开发学习者来说,一次非常有趣的挑战。最初,我是在公众号上看到一篇文章,里面有关于手写Promise的部分内容,当时觉得很新奇,也很有挑战性,所以就决定尝试一下。

在开始着手写Promise之前,我查阅了很多相关资料,对Promise的原理和用法有了比较深入的了解。然后,我开始一步一步地实现Promise,从创建一个简单的Promise类,到实现then方法,再到处理各种异常情况。

二、实现原理

Promise是一个JavaScript对象,它代表着某个异步操作的最终完成或失败及其结果值。Promise对象可以通过then方法来添加回调函数,以便在异步操作完成后执行相应的操作。

Promise的实现原理主要包括以下几个步骤:

  1. 创建一个Promise对象,并传入一个执行器函数。
  2. 在执行器函数中,将异步操作的成功回调函数和失败回调函数作为参数传入。
  3. 当异步操作完成后,调用成功回调函数或失败回调函数,并传入结果值或错误信息。
  4. 在then方法中,将成功回调函数和失败回调函数作为参数传入,以便在异步操作完成后执行相应的操作。

三、Promises/A+规范

Promises/A+规范是Promise对象的一个标准规范,它定义了Promise对象的行为和用法。为了确保自己实现的Promise对象符合标准,我按照Promises/A+规范的要求,对Promise对象进行了测试。

Promises/A+规范包括以下几个方面的测试:

  1. 基本行为测试:测试Promise对象的创建、then方法的调用、成功回调函数和失败回调函数的执行等。
  2. 错误处理测试:测试Promise对象在发生错误时,如何处理错误信息。
  3. then方法的链式调用测试:测试Promise对象then方法的链式调用,以及如何处理多个then方法的执行顺序。
  4. 异步测试:测试Promise对象在异步环境下的行为,以及如何处理异步操作的完成和失败。

四、手写Promise代码

class Promise {
  constructor(executor) {
    this.state = 'pending';
    this.result = undefined;
    this.successCallbacks = [];
    this.failureCallbacks = [];

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

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

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

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

      this.state = 'rejected';
      this.result = error;

      this.failureCallbacks.forEach((callback) => {
        callback(error);
      });
    };

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

  then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        setTimeout(() => {
          try {
            const result = onFulfilled(this.result);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        }, 0);
      } else if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            const result = onRejected(this.result);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        }, 0);
      } else {
        this.successCallbacks.push((value) => {
          setTimeout(() => {
            try {
              const result = onFulfilled(value);
              resolve(result);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });

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

五、测试代码

describe('Promise', () => {
  it('should create a Promise object', () => {
    const promise = new Promise((resolve, reject) => {});
    expect(promise).to.be.an.instanceOf(Promise);
  });

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

    promise.then((value) => {
      expect(value).to.equal('success');
      done();
    });
  });

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

    promise.then(null, (error) => {
      expect(error).to.equal('error');
      done();
    });
  });

  it('should support chaining of then methods', (done) => {
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success');
      }, 100);
    });

    promise
      .then((value) => {
        return value + '!';
      })
      .then((value) => {
        expect(value).to.equal('success!');
        done();
      });
  });

  it('should handle errors in then methods', (done) => {
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('error');
      }, 100);
    });

    promise
      .then(null, (error) => {
        return error + '!';
      })
      .then((value) => {
        expect(value).to.equal('error!');
        done();
      });
  });
});

通过以上这些步骤,我实现了手写Promise,并且通过了Promises/A+规范的所有测试。这让我对Promise对象有了更深入的了解,也对JavaScript的异步编程有了更深刻的体会。

希望这篇文章能给大家带来一些启发,也希望大家能够在学习和实践中不断成长,成为一名优秀的程序员。