Node.js 开始尝试实现 Promise/A

什么是 Promise

Promise 是一种异步编程的解决方案,它可以更优雅地处理异步函数的执行结果。Promise 可以在异步函数的回调函数中返回一个对象,代表这个异步操作的未来结果。通过 then 方法可以处理这个未来的结果,也可以通过 catch 方法处理这个异步操作的失败情况。Promise 的优点是避免了回调函数陷阱,使得异步编程更加清晰明了。

Promise/A 规范

Promise/A 规范是 Promise 实现的一种标准。该规范定义了一个对象的实现接口,即必须实现 then 方法,可以转换成该规范标准的 Promise 实现都可以进行互操作。Promise 实现必须是一个对象,有一个 then 方法,接收两个参数,分别是成功和失败的回调函数,并返回一个新的 Promise 对象。then 方法可以链式调用;如果当前的 Promise 对象返回一个“pending”状态,说明异步操作正在进行中;如果返回一个“fulfilled”状态,说明异步操作成功,并返回一个结果;如果返回一个“rejected”状态,说明异步操作失败,并返回一个错误。

实现 Promise/A

在 Node.js 中实现 Promise/A,首先需要实现一个 Promise 类。该类有一个构造函数,接收一个 Executor 函数作为参数。Executor 函数有两个参数,resolve 和 reject,分别是异步操作成功和失败的回调函数,它们会在异步操作完成后被调用。

class MyPromise {
  constructor(executor) {
    // 初始状态为 pending
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    // 存放成功的回调函数
    this.onResolveCallbacks = [];
    // 存放失败的回调函数
    this.onRejectCallbacks = [];
    const resolve = (value) => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.value = value;
        this.onResolveCallbacks.forEach((callback) => {
          callback();
        });
      }
    }
    const reject = (reason) => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.reason = reason;
        this.onRejectCallbacks.forEach((callback) => {
          callback();
        });
      }
    }
    try {
      executor(resolve, reject);
    } catch(e) {
      reject(e);
    }
  }
}

接下来,需要实现 then 方法。then 方法接收两个参数,onFulfilled 和 onRejected,分别是异步操作成功和失败的回调函数。如果异步操作成功,会依次执行 onFulfilled;如果异步操作失败,会依次执行 onRejected。如果 onFulfilled 和 onRejected 都不是函数,就直接返回当前的 Promise 对象。如果异步操作还没有完成,仍然处于“pending”状态,就把 onFulfilled 和 onRejected 存放到回调函数的队列中;如果异步操作已经完成,就立即执行对应的回调函数。

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  if (typeof onFulfilled !== 'function') {
    onFulfilled = (value) => value;
  }
  if (typeof onRejected !== 'function') {
    onRejected = (reason) => {
      throw reason;
    }
  }
  const promise2 = new MyPromise((resolve, reject) => {
    const resolvePromise = (promise2, x, resolve, reject) => {
      if (promise2 === x) {
        return reject(new TypeError('循环引用'));
      }
      let called = false;
      if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try {
          const then = x.then;
          if (typeof then === 'function') {
            then.call(x, (y) => {
              if (called) {
                return ;
              }
              called = true;
              resolvePromise(promise2, y, resolve, reject);
            }, (r) => {
              if (called) {
                return ;
              }
              called = true;
              reject(r);
            });
          } else {
            resolve(x);
          }
        } catch(e) {
          if (called) {
            return ;
          }
          called = true;
          reject(e);
        }
      } else {
        resolve(x);
      }
    }
    if (this.status === 'fulfilled') {
      setTimeout(() => {
        try {
          const x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch(e) {
          reject(e);
        }
      }, 0);
    }
    if (this.status === 'rejected') {
      setTimeout(() => {
        try {
          const x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch(e) {
          reject(e);
        }
      }, 0);
    }
    if (this.status === 'pending') {
      this.onResolveCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) {
            reject(e);
          }
        }, 0);
      });
      this.onRejectCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) {
            reject(e);
          }
        }, 0);
      });
    }
  });
  return promise2;
}

在实现 then 方法的过程中,需要注意处理 then 方法的链式调用。当调用 then 方法时,需要返回一个新的 Promise 对象,可以让多个 then 方法链式调用。

最后,实现 catch 方法。catch 方法用来捕捉 Promise 中的异常情况,并返回一个新的 Promise 对象。

MyPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
}

使用示例

const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('Hello World');
  }, 1000);
}).then((value) => {
  console.log(value);
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('Promise chain');
    }, 1000);
  });
}).then((value) => {
  console.log(value);
}).catch((reason) => {
  console.log(reason);
});

总结

Promise/A 规范是异步编程的标准之一,使用标准化的 API 可以提高代码的重用性和可维护性。Node.js 开始尝试实现 Promise/A,深入理解其实现原理可以更加灵活地应用到开发中去,提高代码的可读性和可维护性。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a1b31fadd4f0e0ff9ba1f4


纠错反馈