返回

Promise/A+规范: 手写一个Promise polyfill

前端

深入理解 Promise:一步步构建自己的 Polyfill

Promise 的魅力

在现代 JavaScript 编程中,Promise 凭借其简化异步编程、提升代码可读性的优势,已成为不可或缺的工具。然而,在 ES6 之前,JavaScript 并没有原生的 Promise 实现。本文将带你亲手构建一个符合 Promises/A+ 规范的 Promise polyfill,让你从底层领会 Promise 的运作原理。

构建 Promise

1. 常量与工具方法

首先,定义一些常量和工具方法:

  • PENDINGFULFILLEDREJECTED:表示 Promise 的三种状态。
  • isFunctionisObject:判断函数和对象的工具方法。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function isFunction(fn) {
  return typeof fn === 'function';
}

function isObject(obj) {
  return typeof obj === 'object' && obj !== null;
}

2. Promise 构造函数

Promise 构造函数接受一个执行器函数作为参数,通过其提供的 resolvereject 函数改变 Promise 的状态。

class Promise {
  constructor(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(value));
    };

    const reject = (reason) => {
      if (this.state !== PENDING) return;
      this.state = REJECTED;
      this.reason = reason;
      this.onRejectedCallbacks.forEach((callback) => callback(reason));
    };

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

3. Promise 的决议逻辑

3.1 成功决议

then(onFulfilled, onRejected) {
  const promise2 = new Promise((resolve, reject) => {
    if (this.state === FULFILLED) {
      // 当 Promise 已经决议为成功,立即执行 onFulfilled
      queueMicrotask(() => {
        try {
          const x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    } else if (this.state === PENDING) {
      // 当 Promise 还在等待决议,将 onFulfilled 和 onRejected 回调函数存储起来,等待决议后再执行
      this.onFulfilledCallbacks.push(() => {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      });
    }
  });

  return promise2;
}

3.2 失败决议

catch(onRejected) {
  return this.then(null, onRejected);
}

4. Promise.resolve() 和 Promise.reject()

static resolve(value) {
  if (value instanceof Promise) {
    return value;
  }
  return new Promise((resolve) => {
    resolve(value);
  });
}

static reject(reason) {
  return new Promise((resolve, reject) => {
    reject(reason);
  });
}

5. Promise.all() 和 Promise.race()

static all(promises) {
  return new Promise((resolve, reject) => {
    const results = [];
    let pendingCount = promises.length;

    promises.forEach((promise, index) => {
      promise.then(
        (value) => {
          results[index] = value;
          pendingCount--;
          if (pendingCount === 0) {
            resolve(results);
          }
        },
        (reason) => {
          reject(reason);
        }
      );
    });
  });
}

static race(promises) {
  return new Promise((resolve, reject) => {
    promises.forEach((promise) => {
      promise.then(
        (value) => {
          resolve(value);
        },
        (reason) => {
          reject(reason);
        }
      );
    });
  });
}

结语

恭喜你完成了 Promise polyfill 的构建!通过这个过程,你不仅对 Promise 的内部运作有了更深入的理解,还掌握了编写可靠异步代码的技巧。

常见问题解答

  1. Promise 有什么优点?
    Promise 简化了异步编程,提升了代码的可读性和可维护性。

  2. 为什么我们需要 Promise polyfill?
    ES6 之前的 JavaScript 没有原生 Promise 实现,polyfill 弥补了这一空白。

  3. 如何使用 Promise?
    使用 new Promise 创建一个 Promise 实例,并使用 thencatch 等方法处理其决议。

  4. Promise.all() 和 Promise.race() 有什么区别?
    Promise.all() 等待所有 Promise 决议完成,Promise.race() 等待第一个 Promise 决议完成。

  5. 如何测试 Promise polyfill 是否有效?
    使用 promises-aplus-tests 等测试套件验证 polyfill 是否符合 Promises/A+ 规范。