logo

手写实现Promise:从原理到完整代码解析

作者:Nicky2025.09.19 12:55浏览量:0

简介:本文深入解析Promise的核心机制,手写实现一个符合Promise/A+规范的完整版Promise,涵盖状态管理、链式调用、异步调度等关键功能,并提供可运行的代码示例。

手写实现Promise:从原理到完整代码解析

一、Promise的核心机制解析

Promise作为JavaScript异步编程的基石,其核心价值在于将复杂的异步操作封装为可预测的链式调用。根据ECMAScript规范,Promise对象具有三种状态:pending(初始态)、fulfilled(成功态)和rejected(失败态),状态变更具有不可逆性。

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

这段代码实现了Promise的基础状态管理,通过闭包保存状态和回调队列,确保状态变更的原子性。

二、链式调用的核心实现

Promise的链式调用依赖then方法,其关键在于返回新的Promise实例,形成调用链。

2.1 then方法实现

  1. then(onFulfilled, onRejected) {
  2. // 参数可选处理
  3. onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  4. onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
  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. }

2.2 异步调度机制

使用setTimeout将回调推入宏任务队列,确保:

  1. 符合Promise/A+规范的异步执行要求
  2. 避免同步调用导致的状态竞争
  3. 保持与原生Promise一致的调用时序

三、Promise解析过程详解

resolvePromise函数是处理链式调用的核心,其需要处理三种特殊情况:

3.1 解析函数实现

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

3.2 关键处理逻辑

  1. Thenable对象处理:当x是具有then方法的对象时,按Promise规范处理
  2. 循环引用检测:防止Promise实例自我引用导致的无限循环
  3. 调用状态锁:通过called标志确保resolve/reject只调用一次

四、完整类方法实现

4.1 静态方法实现

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

4.2 实例方法扩展

  1. // catch方法实现
  2. catch(onRejected) {
  3. return this.then(null, onRejected);
  4. }
  5. // finally方法实现
  6. finally(callback) {
  7. return this.then(
  8. value => MyPromise.resolve(callback()).then(() => value),
  9. reason => MyPromise.resolve(callback()).then(() => { throw reason; })
  10. );
  11. }

五、测试验证与规范符合性

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

  1. # 安装测试工具
  2. npm install promises-aplus-tests -g
  3. # 创建适配层
  4. function adapter(promise) {
  5. return {
  6. then: (onFulfilled, onRejected) => {
  7. return promise.then(onFulfilled, onRejected);
  8. }
  9. };
  10. }
  11. # 运行测试
  12. promises-aplus-tests ./adapter.js

六、性能优化建议

  1. 微任务调度:可使用MutationObserverqueueMicrotask替代setTimeout提升性能
  2. 内存优化:在状态变更后清空回调队列
  3. 错误边界处理:添加全局未捕获异常处理器

七、实际应用场景示例

  1. // 图片懒加载实现
  2. function loadImage(url) {
  3. return new MyPromise((resolve, reject) => {
  4. const img = new Image();
  5. img.onload = () => resolve(img);
  6. img.onerror = () => reject(new Error(`Image load failed: ${url}`));
  7. img.src = url;
  8. });
  9. }
  10. // 分页数据加载
  11. function fetchPage(page) {
  12. return new MyPromise(resolve => {
  13. setTimeout(() => {
  14. resolve({ page, data: Array(10).fill(0).map((_,i)=>i+page*10) });
  15. }, 500);
  16. });
  17. }

通过手写实现Promise,开发者不仅能深入理解异步编程的核心机制,更能获得以下收益:

  1. 精准控制异步流程的执行时序
  2. 自定义错误处理和调试钩子
  3. 针对特定场景的性能优化
  4. 与现有异步库的无缝集成能力

完整实现代码约200行,通过模块化设计可轻松扩展为支持取消、超时等高级功能的增强版Promise库。

相关文章推荐

发表评论