logo

从零实现Promise:手写一个符合规范的Promise类库

作者:问题终结者2025.09.19 12:47浏览量:0

简介:本文通过手写Promise实现,深入解析Promise核心机制,涵盖状态管理、链式调用、异步调度及错误处理,帮助开发者掌握异步编程的核心原理。

从零实现Promise:手写一个符合规范的Promise类库

Promise作为JavaScript异步编程的核心解决方案,自ES6规范发布以来已成为前端开发的标配。然而,许多开发者仅停留在API调用层面,对其内部实现机制缺乏深入理解。本文将通过手写一个符合Promise/A+规范的实现,系统解析Promise的核心原理,帮助读者掌握状态管理、链式调用、异步调度等关键技术点。

一、Promise核心机制解析

1.1 状态机模型实现

Promise规范定义了三种状态:pending、fulfilled和rejected。状态转换具有单向性,一旦从pending变为fulfilled/rejected,便不可更改。这种设计确保了异步操作的确定性。

  1. class MyPromise {
  2. constructor(executor) {
  3. this.state = 'pending'; // 初始状态
  4. this.value = undefined; // 成功值
  5. this.reason = undefined; // 失败原因
  6. this.onFulfilledCallbacks = []; // 成功回调队列
  7. this.onRejectedCallbacks = []; // 失败回调队列
  8. const resolve = (value) => {
  9. if (this.state === 'pending') {
  10. this.state = 'fulfilled';
  11. this.value = value;
  12. this.onFulfilledCallbacks.forEach(fn => fn());
  13. }
  14. };
  15. const reject = (reason) => {
  16. if (this.state === 'pending') {
  17. this.state = 'rejected';
  18. this.reason = reason;
  19. this.onRejectedCallbacks.forEach(fn => fn());
  20. }
  21. };
  22. try {
  23. executor(resolve, reject);
  24. } catch (err) {
  25. reject(err);
  26. }
  27. }
  28. }

状态管理实现要点:

  • 严格的状态检查(仅pending可转换)
  • 回调队列的延迟执行机制
  • 同步错误的捕获处理

1.2 链式调用实现原理

Promise的then方法支持链式调用,这依赖于返回值的新Promise实例创建。每个then调用都会返回一个新Promise,形成调用链。

  1. then(onFulfilled, onRejected) {
  2. // 参数默认值处理
  3. onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  4. onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
  5. const promise2 = new MyPromise((resolve, reject) => {
  6. if (this.state === 'fulfilled') {
  7. setTimeout(() => {
  8. try {
  9. const x = onFulfilled(this.value);
  10. resolvePromise(promise2, x, resolve, reject);
  11. } catch (e) {
  12. reject(e);
  13. }
  14. }, 0);
  15. } else if (this.state === 'rejected') {
  16. setTimeout(() => {
  17. try {
  18. const x = onRejected(this.reason);
  19. resolvePromise(promise2, x, resolve, reject);
  20. } catch (e) {
  21. reject(e);
  22. }
  23. }, 0);
  24. } else if (this.state === 'pending') {
  25. this.onFulfilledCallbacks.push(() => {
  26. setTimeout(() => {
  27. try {
  28. const x = onFulfilled(this.value);
  29. resolvePromise(promise2, x, resolve, reject);
  30. } catch (e) {
  31. reject(e);
  32. }
  33. }, 0);
  34. });
  35. this.onRejectedCallbacks.push(() => {
  36. setTimeout(() => {
  37. try {
  38. const x = onRejected(this.reason);
  39. resolvePromise(promise2, x, resolve, reject);
  40. } catch (e) {
  41. reject(e);
  42. }
  43. }, 0);
  44. });
  45. }
  46. });
  47. return promise2;
  48. }

链式调用关键点:

  • 异步执行回调(通过setTimeout模拟微任务)
  • 回调函数的错误捕获
  • 新Promise的返回值处理

二、Promise/A+规范核心实现

2.1 返回值处理规范

根据Promise/A+规范,then方法的返回值需要特殊处理,可能返回普通值、Promise对象或抛出异常。

  1. function resolvePromise(promise2, x, resolve, reject) {
  2. // 防止循环引用
  3. if (promise2 === x) {
  4. return reject(new TypeError('Chaining cycle detected for promise'));
  5. }
  6. let called = false;
  7. // 处理x为Promise的情况
  8. if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
  9. try {
  10. const then = x.then;
  11. if (typeof then === 'function') {
  12. then.call(
  13. x,
  14. y => {
  15. if (called) return;
  16. called = true;
  17. resolvePromise(promise2, y, resolve, reject);
  18. },
  19. r => {
  20. if (called) return;
  21. called = true;
  22. reject(r);
  23. }
  24. );
  25. } else {
  26. resolve(x);
  27. }
  28. } catch (e) {
  29. if (called) return;
  30. called = true;
  31. reject(e);
  32. }
  33. } else {
  34. resolve(x);
  35. }
  36. }

返回值处理要点:

  • 循环引用检测
  • thenable对象识别
  • 多次调用保护
  • 异常捕获机制

2.2 静态方法实现

Promise类提供了多个静态方法,其中Promise.resolve和Promise.reject是基础实现。

  1. static resolve(value) {
  2. if (value instanceof MyPromise) {
  3. return value;
  4. }
  5. return new MyPromise(resolve => resolve(value));
  6. }
  7. static reject(reason) {
  8. return new MyPromise((_, reject) => reject(reason));
  9. }

三、进阶功能实现

3.1 错误冒泡机制

未捕获的Promise拒绝需要特殊处理,现代浏览器会将其打印到控制台。

  1. class MyPromise {
  2. constructor(executor) {
  3. // ...原有实现...
  4. this.initUnhandledRejection();
  5. }
  6. initUnhandledRejection() {
  7. const rejectError = this.reason;
  8. if (this.state === 'rejected' &&
  9. !this.onRejectedCallbacks.some(cb => cb.length > 0)) {
  10. setTimeout(() => {
  11. if (this.state === 'rejected') {
  12. console.error('UnhandledPromiseRejectionWarning:', rejectError);
  13. }
  14. }, 0);
  15. }
  16. }
  17. }

3.2 微任务队列模拟

由于浏览器环境限制,我们使用MutationObserver模拟微任务:

  1. function asyncSchedule(fn) {
  2. if (typeof MutationObserver !== 'undefined') {
  3. const observer = new MutationObserver(fn);
  4. const node = document.createElement('div');
  5. observer.observe(node, { attributes: true });
  6. node.setAttribute('x', 'y');
  7. setTimeout(() => observer.disconnect(), 0);
  8. } else {
  9. setTimeout(fn, 0); // 降级方案
  10. }
  11. }

四、完整实现与测试

4.1 完整类实现

将上述模块整合后的完整实现:

  1. class MyPromise {
  2. // ...前文所有代码片段整合...
  3. // 添加catch方法
  4. catch(onRejected) {
  5. return this.then(null, onRejected);
  6. }
  7. // 添加finally方法
  8. finally(callback) {
  9. return this.then(
  10. value => MyPromise.resolve(callback()).then(() => value),
  11. reason => MyPromise.resolve(callback()).then(() => { throw reason; })
  12. );
  13. }
  14. // 静态all方法
  15. static all(promises) {
  16. return new MyPromise((resolve, reject) => {
  17. const results = [];
  18. let count = 0;
  19. if (promises.length === 0) {
  20. resolve(results);
  21. }
  22. promises.forEach((promise, index) => {
  23. MyPromise.resolve(promise).then(
  24. value => {
  25. results[index] = value;
  26. count++;
  27. if (count === promises.length) {
  28. resolve(results);
  29. }
  30. },
  31. reason => reject(reason)
  32. );
  33. });
  34. });
  35. }
  36. }

4.2 规范测试用例

使用Promises/A+测试套件验证实现:

  1. // 测试用例示例
  2. describe('Promise基本功能', () => {
  3. it('应该正确处理同步成功', () => {
  4. const promise = new MyPromise(resolve => resolve(1));
  5. return promise.then(val => expect(val).toBe(1));
  6. });
  7. it('应该支持链式调用', () => {
  8. let order = [];
  9. return new MyPromise(resolve => {
  10. order.push(1);
  11. resolve();
  12. }).then(() => {
  13. order.push(2);
  14. return new MyPromise(resolve => resolve());
  15. }).then(() => {
  16. order.push(3);
  17. expect(order).toEqual([1, 2, 3]);
  18. });
  19. });
  20. });

五、实际应用建议

  1. 调试技巧

    • 在resolve/reject处添加日志
    • 使用Promise.allSettled处理部分失败场景
    • 实现自定义的Promise.retry方法
  2. 性能优化

    • 避免在then回调中创建不必要的Promise
    • 合理使用Promise.race实现超时控制
    • 批量处理并行Promise
  3. 错误处理最佳实践

    • 始终处理Promise拒绝
    • 使用async/await改善可读性
    • 实现全局未处理拒绝监听
  1. // 全局未处理拒绝监听示例
  2. window.addEventListener('unhandledrejection', event => {
  3. console.warn('未处理的Promise拒绝:', event.reason);
  4. // 可以在这里添加上报逻辑
  5. });

通过手写Promise实现,开发者不仅能深入理解异步编程的核心机制,还能在实际项目中更灵活地运用Promise。建议结合ES6规范文档和实际业务场景,不断完善和优化自定义Promise实现。

相关文章推荐

发表评论