logo

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

作者:问答酱2025.09.19 12:47浏览量:0

简介:本文通过手写Promise实现,详细解析其核心机制、状态管理、链式调用及异步处理,帮助开发者深入理解Promise工作原理,提升实际开发能力。

一、面试场景中的Promise手写要求

在前端开发面试中,”手写Promise”已成为高频考点。这一要求不仅考察候选人对JavaScript异步编程的理解深度,更验证其能否将理论转化为可运行的代码实现。从企业招聘角度看,这反映了现代前端开发对基础扎实、具备底层思维工程师的需求。

二、Promise核心机制解析

1. 状态机的三态模型

Promise规范定义了三种状态:

  • Pending:初始状态,可转换为Fulfilled或Rejected
  • Fulfilled:操作成功完成,状态不可逆
  • Rejected:操作失败,状态不可逆

这种设计遵循了”一次到位”原则,确保异步操作的确定性结果。状态转换必须通过resolve()reject()方法触发,外部代码无法直接修改状态。

2. 微任务队列机制

Promise的回调执行采用微任务(Microtask)队列,优先级高于宏任务(Macrotask)。当调用then()注册回调时,回调会被推入微任务队列,在当前执行栈清空后立即执行。这种设计避免了回调地狱,同时保证了异步操作的顺序性。

三、手写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. // 实现状态转换和回调执行
  10. };
  11. const reject = (reason) => {
  12. // 实现状态转换和回调执行
  13. };
  14. try {
  15. executor(resolve, reject);
  16. } catch (err) {
  17. reject(err);
  18. }
  19. }
  20. }

2. 状态管理实现

  1. resolve = (value) => {
  2. if (this.state === 'pending') {
  3. this.state = 'fulfilled';
  4. this.value = value;
  5. // 执行所有成功回调
  6. this.onFulfilledCallbacks.forEach(fn => fn());
  7. }
  8. };
  9. reject = (reason) => {
  10. if (this.state === 'pending') {
  11. this.state = 'rejected';
  12. this.reason = reason;
  13. // 执行所有失败回调
  14. this.onRejectedCallbacks.forEach(fn => fn());
  15. }
  16. };

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

4. 链式调用关键:resolvePromise

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

四、完整实现代码

  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. then(onFulfilled, onRejected) {
  29. onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  30. onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
  31. const promise2 = new MyPromise((resolve, reject) => {
  32. if (this.state === 'fulfilled') {
  33. setTimeout(() => {
  34. try {
  35. const x = onFulfilled(this.value);
  36. resolvePromise(promise2, x, resolve, reject);
  37. } catch (e) {
  38. reject(e);
  39. }
  40. }, 0);
  41. } else if (this.state === 'rejected') {
  42. setTimeout(() => {
  43. try {
  44. const x = onRejected(this.reason);
  45. resolvePromise(promise2, x, resolve, reject);
  46. } catch (e) {
  47. reject(e);
  48. }
  49. }, 0);
  50. } else if (this.state === 'pending') {
  51. this.onFulfilledCallbacks.push(() => {
  52. setTimeout(() => {
  53. try {
  54. const x = onFulfilled(this.value);
  55. resolvePromise(promise2, x, resolve, reject);
  56. } catch (e) {
  57. reject(e);
  58. }
  59. }, 0);
  60. });
  61. this.onRejectedCallbacks.push(() => {
  62. setTimeout(() => {
  63. try {
  64. const x = onRejected(this.reason);
  65. resolvePromise(promise2, x, resolve, reject);
  66. } catch (e) {
  67. reject(e);
  68. }
  69. }, 0);
  70. });
  71. }
  72. });
  73. return promise2;
  74. }
  75. }
  76. function resolvePromise(promise2, x, resolve, reject) {
  77. if (promise2 === x) {
  78. return reject(new TypeError('Chaining cycle detected for promise'));
  79. }
  80. let called = false;
  81. if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
  82. try {
  83. const then = x.then;
  84. if (typeof then === 'function') {
  85. then.call(
  86. x,
  87. y => {
  88. if (called) return;
  89. called = true;
  90. resolvePromise(promise2, y, resolve, reject);
  91. },
  92. r => {
  93. if (called) return;
  94. called = true;
  95. reject(r);
  96. }
  97. );
  98. } else {
  99. resolve(x);
  100. }
  101. } catch (e) {
  102. if (called) return;
  103. called = true;
  104. reject(e);
  105. }
  106. } else {
  107. resolve(x);
  108. }
  109. }

五、实际应用建议

  1. 调试技巧:在实现过程中,建议使用console.log跟踪状态变化和回调执行顺序,这有助于理解异步流程
  2. 测试用例设计
    • 立即resolve/reject的测试
    • 延迟resolve/reject的测试
    • 链式调用的测试
    • 异常处理的测试
  3. 性能优化:在实际项目中,可以考虑使用queueMicrotask替代setTimeout实现更高效的微任务调度

六、进阶方向

  1. 实现catchfinally等附加方法
  2. 添加静态方法allraceallSettled
  3. 支持异步生成器(Async Generator)集成
  4. 实现Promise的取消功能(Cancelable Promise)

通过手写Promise实现,开发者不仅能深入理解异步编程的核心机制,更能培养解决复杂问题的能力。这种底层思维在处理复杂异步场景、调试性能问题时具有不可替代的价值。建议开发者在掌握基础实现后,继续探索Promise的扩展应用,如与Async/Await的协同使用、在React/Vue中的状态管理等高级场景。

相关文章推荐

发表评论