手写Promises

0 阅读4分钟

—— 从零实现一个符合 Promises/A+ 规范的完整 MyPromise

作者:AI 助教 | 版本:1.0 | 适合人群:想彻底掌握异步编程的前端开发者


💬 “学会用 Promise 的人很多,真正懂得它的人很少。”
本指南将带你 一行一行构建自己的 MyPromise,不仅“会写”,更要“懂为什么这么写”。


🎯 一、目标:我们最终要实现什么?

我们要实现一个功能完整的 MyPromise,支持:

.then() 链式调用
.catch() 错误捕获
Promise.resolve() / reject()
Promise.all() / race()
✅ 支持异步任务和 thenable 对象
✅ 符合事件循环规范(异步执行回调)
✅ 通过所有边界测试

👉 最终代码可直接用于学习、面试、教学。


🧱 二、核心结构设计

class MyPromise {
  constructor(executor) { /* ... */ }
  then(onFulfilled, onRejected) { /* ... */ }
  catch(onRejected) { /* ... */ }

  static resolve(value) { /* ... */ }
  static reject(reason) { /* ... */ }
  static all(promises) { /* ... */ }
  static race(promises) { /* ... */ }

  resolvePromise(promise2, x, resolve, reject) { /* ... */ }
}

🔧 三、第一步:最简状态机

1. 定义状态常量

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

2. 构造函数骨架

class MyPromise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;      // 成功值
    this.reason = undefined;     // 失败原因
    this.onFulfilledCallbacks = []; // 成功回调队列
    this.onRejectedCallbacks = [];  // 失败回调队列

    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

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

📌 关键点:

  • 状态只能变一次
  • 使用数组缓存回调 → 实现发布-订阅模式

⏳ 四、第二步:实现 .then() 基础功能

目标:支持同步/异步 resolve

then(onFulfilled, onRejected) {
  // 默认值处理
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
  onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };

  if (this.status === FULFILLED) {
    onFulfilled(this.value);
  } else if (this.status === REJECTED) {
    onRejected(this.reason);
  } else if (this.status === PENDING) {
    this.onFulfilledCallbacks.push(() => onFulfilled(this.value));
    this.onRejectedCallbacks.push(() => onRejected(this.reason));
  }
}

⚠️ 当前问题:

  • .then 回调是同步执行的 ❌
  • 不支持链式调用 ❌

⏱️ 五、第三步:强制异步执行(事件循环兼容)

修改 .then 所有分支:

if (this.status === FULFILLED) {
  setTimeout(() => {
    onFulfilled(this.value);
  }, 0);
} else if (this.status === REJECTED) {
  setTimeout(() => {
    onRejected(this.reason);
  }, 0);
} else if (this.status === PENDING) {
  this.onFulfilledCallbacks.push(() => {
    setTimeout(() => {
      onFulfilled(this.value);
    }, 0);
  });
  // 同理处理 rejected
}

✅ 现在无论同步或异步 resolve,.then 都会异步执行!


🔗 六、第四步:实现链式调用(返回新 Promise)

核心思想:每个 .then 返回一个新的 MyPromise

then(onFulfilled, onRejected) {
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
  onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };

  const promise2 = new MyPromise((resolve, reject) => {
    if (this.status === FULFILLED) {
      setTimeout(() => {
        try {
          const x = onFulfilled(this.value);
          this.resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    } else if (this.status === REJECTED) {
      setTimeout(() => {
        try {
          const x = onRejected(this.reason);
          this.resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    } else if (this.status === PENDING) {
      this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });
      this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });
    }
  });

  return promise2;
}

🌟 七、第五步:核心 resolvePromise 方法

功能:统一解析 .then 回调的返回值

resolvePromise(promise2, x, resolve, reject) {
  // 1. 循环引用检测
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }

  let called = false; // 防止多次调用 resolve/reject

  // 2. 只有对象或函数才可能是 thenable
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      const then = x.then;

      // 3. 如果是函数,认为是 Promise/thenable
      if (typeof then === 'function') {
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            this.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); // 基本类型
  }
}

📌 这是整个 Promise 的灵魂函数。


🧩 八、第六步:添加辅助方法

1. catch 方法

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

2. Promise.resolve

static resolve(value) {
  return new MyPromise(resolve => resolve(value));
}

3. Promise.reject

static reject(reason) {
  return new MyPromise((_, reject) => reject(reason));
}

4. Promise.all

static all(promises) {
  return new MyPromise((resolve, reject) => {
    const results = [];
    let count = 0;

    if (promises.length === 0) return resolve(results);

    promises.forEach((p, index) => {
      MyPromise.resolve(p).then(
        value => {
          results[index] = value;
          count++;
          if (count === promises.length) resolve(results);
        },
        reject // 任意失败就整体失败
      );
    });
  });
}

5. Promise.race

static race(promises) {
  return new MyPromise((resolve, reject) => {
    promises.forEach(p => {
      MyPromise.resolve(p).then(resolve, reject);
    });
  });
}

✅ 九、完整代码(可运行版)

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

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

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };

    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }

  resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
      return reject(new TypeError('Chaining cycle detected'));
    }

    let called = false;

    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      try {
        const then = x.then;
        if (typeof then === 'function') {
          then.call(x,
            y => {
              if (called) return;
              called = true;
              this.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);
    }
  }

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

  static resolve(value) {
    return new MyPromise(resolve => resolve(value));
  }

  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason));
  }

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = [];
      let count = 0;
      if (promises.length === 0) return resolve(results);
      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(
          value => {
            results[index] = value;
            count++;
            if (count === promises.length) resolve(results);
          },
          reject
        );
      });
    });
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(p => {
        MyPromise.resolve(p).then(resolve, reject);
      });
    });
  }
}

export default MyPromise;

🧪 十、测试用例大全

// 测试1:基本链式
MyPromise.resolve(1).then(x => x + 1).then(console.log); // 2

// 测试2:异步 resolve
new MyPromise(r => setTimeout(() => r(1), 10)).then(console.log); // 1

// 测试3:错误处理
MyPromise.reject('err').catch(console.log); // err

// 测试4:返回 Promise
MyPromise.resolve(1).then(() => MyPromise.resolve(2)).then(console.log); // 2

// 测试5:循环引用
const p = MyPromise.resolve(1);
const p2 = p.then(() => p2);
p2.catch(console.log); // TypeError

// 测试6:all
MyPromise.all([1, 2, MyPromise.resolve(3)]).then(console.log); // [1,2,3]

// 测试7:race
MyPromise.race([MyPromise.reject('fail'), MyPromise.resolve('win')])
  .catch(console.log); // fail

🎁 附录:记忆口诀

🔤 “一构二态三回调,链式靠它串成桥”
🔁 “是自己?报错!有 then?等它!不是?直接发!”
🔐 “叫过一次就不能再叫了”


❤️ 结语

你已经完成了前端进阶路上的一次重大突破。
现在你不仅能写出 Promise,还能向别人讲解它的原理。

真正的掌握,不是记住代码,而是能从无到有创造它。

继续加油,你正在成为那个定义规则的人 💪✨