logo

手写Promise:从原理到实现的深度解析

作者:demo2025.09.19 12:55浏览量:1

简介:本文深入解析Promise的核心机制,通过手写实现完整代码,帮助开发者理解异步编程的底层原理,提升代码控制能力。

一、Promise的核心价值与实现意义

Promise作为ES6引入的异步编程解决方案,彻底改变了JavaScript回调地狱的困境。其核心价值体现在三个方面:

  1. 异步流程标准化:通过then/catch/finally方法链式调用,将分散的回调函数统一管理
  2. 状态不可逆性:pending→fulfilled/rejected的转变确保结果确定性
  3. 错误传播机制:链式调用中自动传递reject状态,避免手动错误处理

手写Promise的实现过程,本质是对事件循环、微任务队列、状态机等核心概念的深度实践。相较于直接使用原生Promise,手写实现能帮助开发者

  • 深入理解异步编程的底层机制
  • 掌握状态管理的最佳实践
  • 提升对thenable对象的处理能力
  • 理解async/await的编译原理

二、Promise基础架构设计

1. 构造函数与状态管理

  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. }

关键设计点

  • 状态机实现:通过严格的状态检查确保不可逆性
  • 异步回调队列:使用数组存储回调函数,解决同步调用问题
  • 错误捕获机制:try-catch包裹executor防止意外错误

2. then方法实现与链式调用

  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. }

实现要点解析

  1. 微任务模拟:使用setTimeout模拟微任务队列(实际实现应使用queueMicrotask)
  2. 值穿透处理:当onFulfilled/onRejected非函数时提供默认处理
  3. 异步执行保障:所有回调都通过异步方式执行,确保符合Promise规范

三、核心机制深度实现

1. resolvePromise解析

  1. function resolvePromise(promise2, x, resolve, reject) {
  2. // 循环引用检查
  3. if (promise2 === x) {
  4. return reject(new TypeError('Chaining cycle detected for promise'));
  5. }
  6. // 防止多次调用
  7. let called = false;
  8. if ((typeof x === 'object' && x !== null) || 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对象时的安全调用
  • 防止then方法调用时的异常抛出
  • 解决Promise的循环引用问题
  • 确保resolve/reject只调用一次

2. 静态方法实现

catch方法

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

finally方法

  1. finally(callback) {
  2. return this.then(
  3. value => MyPromise.resolve(callback()).then(() => value),
  4. reason => MyPromise.resolve(callback()).then(() => { throw reason })
  5. );
  6. }

resolve/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((resolve, reject) => reject(reason));
  9. }

四、完整实现与测试验证

完整代码实现

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

测试用例设计

  1. // 基础功能测试
  2. const promise = new MyPromise((resolve, reject) => {
  3. setTimeout(() => resolve('成功'), 1000);
  4. });
  5. promise
  6. .then(res => {
  7. console.log(res); // 1秒后输出"成功"
  8. return '新值';
  9. })
  10. .then(res => {
  11. console.log(res); // 输出"新值"
  12. });
  13. // 静态方法测试
  14. MyPromise.all([
  15. MyPromise.resolve(1),
  16. new MyPromise(resolve => setTimeout(() => resolve(2), 500)),
  17. 3
  18. ]).then(res => console.log(res)); // [1, 2, 3]
  19. // 异常处理测试
  20. new MyPromise((resolve, reject) => {
  21. throw new Error('错误');
  22. }).catch(err => console.log(err.message)); // "错误"

五、性能优化与最佳实践

1. 微任务优化方案

实际实现中应替换setTimeout为:

  1. // 方案1:使用MutationObserver(现代浏览器)
  2. const queueMicrotask = (() => {
  3. const queue = [];
  4. const observer = new MutationObserver(() => {
  5. const task = queue.shift();
  6. if (task) task();
  7. });
  8. const node = document.createTextNode('');
  9. observer.observe(node, { characterData: true });
  10. return callback => {
  11. queue.push(callback);
  12. node.data = Math.random();
  13. };
  14. })();
  15. // 方案2:使用MessageChannel(兼容性更好)
  16. const channel = new MessageChannel();
  17. const queueMicrotask = callback => {
  18. channel.port2.onmessage = () => {
  19. channel.port2.onmessage = null;
  20. callback();
  21. };
  22. channel.port1.postMessage(null);
  23. };

2. 内存管理优化

  • 及时清理已完成Promise的回调队列
  • 对thenable对象进行缓存处理
  • 实现弱引用机制防止内存泄漏

3. 调试支持增强

  1. class DebugPromise extends MyPromise {
  2. constructor(executor) {
  3. super(executor);
  4. this.creationStack = new Error().stack;
  5. }
  6. then(onFulfilled, onRejected) {
  7. const promise = super.then(onFulfilled, onRejected);
  8. promise.creationStack = this.creationStack;
  9. return promise;
  10. }
  11. }

六、实际应用场景分析

1. API请求封装

  1. function fetchData(url) {
  2. return new MyPromise((resolve, reject) => {
  3. const xhr = new XMLHttpRequest();
  4. xhr.open('GET', url);
  5. xhr.onload = () => resolve(xhr.responseText);
  6. xhr.onerror = () => reject(new Error('Network error'));
  7. xhr.send();
  8. });
  9. }
  10. // 使用示例
  11. fetchData('/api/data')
  12. .then(parseJSON)
  13. .then(processData)
  14. .catch(handleError);

2. 动画序列控制

  1. function animate(element, styles, duration) {
  2. return new MyPromise(resolve => {
  3. element.style.transition = `all ${duration}ms`;
  4. Object.assign(element.style, styles);
  5. setTimeout(resolve, duration);
  6. });
  7. }
  8. // 序列动画
  9. animate(div1, { opacity: 0 }, 500)
  10. .then(() => animate(div2, { transform: 'translateX(100px)' }, 300))
  11. .then(() => console.log('动画完成'));

3. 资源加载管理

  1. function loadResources(resources) {
  2. return MyPromise.all(
  3. resources.map(src => {
  4. return new MyPromise((resolve, reject) => {
  5. const tag = src.endsWith('.js') ? 'script' : 'link';
  6. const element = document.createElement(tag);
  7. element.src = src;
  8. element.onload = resolve;
  9. element.onerror = () => reject(new Error(`Failed to load ${src}`));
  10. document.head.appendChild(element);
  11. });
  12. })
  13. );
  14. }

七、常见问题与解决方案

1. 内存泄漏问题

症状:长时间运行的页面中Promise对象持续增加
解决方案

  • 实现自动清理机制
  • 使用WeakMap存储关联数据
  • 避免在闭包中保留不必要引用

2. 错误处理遗漏

症状:未捕获的Promise rejection导致控制台警告
解决方案

  • 添加全局unhandledrejection事件监听
    1. window.addEventListener('unhandledrejection', e => {
    2. console.error('未处理的Promise错误:', e.reason);
    3. });
  • 在应用顶层添加错误边界处理

3. 性能瓶颈分析

优化方向

  • 减少不必要的Promise包装
  • 合并连续的then调用
  • 使用async/await简化复杂链式调用

八、进阶实现方向

1. 支持CancelToken

  1. class CancelablePromise extends MyPromise {
  2. constructor(executor) {
  3. super((resolve, reject) => {
  4. this.cancel = () => {
  5. if (this.state === 'pending') {
  6. this.state = 'rejected';
  7. this.reason = new Error('Promise canceled');
  8. // 触发拒绝回调
  9. this.onRejectedCallbacks.forEach(fn => fn());
  10. }
  11. };
  12. executor(resolve, reject);
  13. });
  14. }
  15. }

2. 实现进度通知

  1. class ProgressPromise extends MyPromise {
  2. constructor(executor) {
  3. super((resolve, reject) => {
  4. this.progressCallbacks = [];
  5. const progress = (value) => {
  6. this.progressCallbacks.forEach(fn => fn(value));
  7. };
  8. executor(resolve, reject, progress);
  9. });
  10. onProgress(callback) {
  11. this.progressCallbacks.push(callback);
  12. return this;
  13. }
  14. }
  15. }

3. 并发控制实现

  1. class PromisePool {
  2. constructor(maxConcurrent) {
  3. this.maxConcurrent = maxConcurrent;
  4. this.running = 0;
  5. this.queue = [];
  6. }
  7. add(promiseCreator) {
  8. return new MyPromise((resolve, reject) => {
  9. const run = () => {
  10. this.running++;
  11. promiseCreator()
  12. .then(resolve, reject)
  13. .finally(() => {
  14. this.running--;
  15. if (this.queue.length > 0) {
  16. const next = this.queue.shift();
  17. next();
  18. }
  19. });
  20. };
  21. if (this.running < this.maxConcurrent) {
  22. run();
  23. } else {
  24. this.queue.push(run);
  25. }
  26. });
  27. }
  28. }

九、总结与学习建议

核心收获

  1. 深入理解Promise状态机实现原理
  2. 掌握异步编程的底层控制方法
  3. 提升对JavaScript事件循环的理解
  4. 获得自定义异步流程控制的能力

学习路径建议

  1. 基础阶段:先理解原生Promise的使用方法
  2. 实践阶段:尝试手写简化版Promise(仅实现then和状态管理)
  3. 进阶阶段:完善resolvePromise等核心方法
  4. 优化阶段:研究性能优化和调试支持

推荐资源

  1. Promises/A+规范文档
  2. JavaScript高级程序设计(第4版)第11章
  3. MDN Promise文档深入阅读
  4. 实际项目中的Promise使用案例分析

通过系统学习Promise的实现原理,开发者不仅能更高效地使用异步编程,还能在复杂场景下获得更强的控制能力,为开发高性能、可维护的JavaScript应用打下坚实基础。

相关文章推荐

发表评论