logo

手写Promise全解析:从原理到面试实战指南

作者:快去debug2025.09.19 12:47浏览量:0

简介:本文深度解析前端面试高频题——手写Promise实现,从Promise/A+规范核心原理出发,逐步拆解状态管理、链式调用、异步处理等关键实现细节,提供可直接用于面试的代码模板及调试技巧。

前端面试100道手写题(1)—— 手写Promise实现

一、为什么面试必考手写Promise?

在前端技术栈中,Promise作为异步编程的核心解决方案,其实现原理考察能直接反映开发者对事件循环、状态管理、闭包等核心概念的理解深度。据统计,2023年大厂前端面试中,83%的高级岗位要求候选人具备手写Promise的能力。

Promise的实现涉及三个关键技术点:

  1. 状态机模型:pending/fulfilled/rejected三态转换
  2. 观察者模式:then方法的链式调用机制
  3. 微任务队列:与macrotask的调度差异

二、Promise核心规范解析

根据Promise/A+规范,实现需满足以下强制要求:

  1. 状态不可逆:一旦从pending转为fulfilled/rejected,不得再变更
  2. 值穿透:then方法返回的新Promise会继承前一个Promise的解析值
  3. 异步保证:即使同步调用resolve,then的回调也必须异步执行

规范中的关键术语:

  • [[Resolve]]:内部解析函数
  • onFulfilled/onRejected:回调函数数组
  • settled:状态确定后的不可变状态

三、手写实现分步拆解

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. // 异步执行回调
  13. queueMicrotask(() => {
  14. this.onFulfilledCallbacks.forEach(fn => fn());
  15. });
  16. }
  17. };
  18. const reject = (reason) => {
  19. if (this.state === 'pending') {
  20. this.state = 'rejected';
  21. this.reason = reason;
  22. queueMicrotask(() => {
  23. this.onRejectedCallbacks.forEach(fn => fn());
  24. });
  25. }
  26. };
  27. try {
  28. executor(resolve, reject);
  29. } catch (err) {
  30. reject(err);
  31. }
  32. }
  33. }

实现要点

  • 使用queueMicrotask确保微任务调度
  • 状态变更时进行防御性检查
  • 异常捕获机制防止executor出错

2. 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. queueMicrotask(() => {
  8. try {
  9. const x = onFulfilled(this.value);
  10. resolvePromise(promise2, x, resolve, reject);
  11. } catch (e) {
  12. reject(e);
  13. }
  14. });
  15. } else if (this.state === 'rejected') {
  16. queueMicrotask(() => {
  17. try {
  18. const x = onRejected(this.reason);
  19. resolvePromise(promise2, x, resolve, reject);
  20. } catch (e) {
  21. reject(e);
  22. }
  23. });
  24. } else if (this.state === 'pending') {
  25. this.onFulfilledCallbacks.push(() => {
  26. queueMicrotask(() => {
  27. try {
  28. const x = onFulfilled(this.value);
  29. resolvePromise(promise2, x, resolve, reject);
  30. } catch (e) {
  31. reject(e);
  32. }
  33. });
  34. });
  35. this.onRejectedCallbacks.push(() => {
  36. queueMicrotask(() => {
  37. try {
  38. const x = onRejected(this.reason);
  39. resolvePromise(promise2, x, resolve, reject);
  40. } catch (e) {
  41. reject(e);
  42. }
  43. });
  44. });
  45. }
  46. });
  47. return promise2;
  48. }

关键处理逻辑

  1. 回调函数参数默认值处理
  2. 状态区分处理(同步/异步)
  3. 使用queueMicrotask保证异步性
  4. 通过resolvePromise处理值穿透

3. resolvePromise解析函数(规范核心)

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

规范要点实现

  1. 循环引用检测
  2. thenable对象处理
  3. 一次调用保证(called标志)
  4. 异常捕获机制

四、完整实现代码

  1. class MyPromise {
  2. // ... 前述构造函数代码 ...
  3. catch(onRejected) {
  4. return this.then(null, onRejected);
  5. }
  6. static resolve(value) {
  7. if (value instanceof MyPromise) {
  8. return value;
  9. }
  10. return new MyPromise(resolve => resolve(value));
  11. }
  12. static reject(reason) {
  13. return new MyPromise((_, reject) => reject(reason));
  14. }
  15. static all(promises) {
  16. return new MyPromise((resolve, reject) => {
  17. const results = [];
  18. let completed = 0;
  19. if (promises.length === 0) {
  20. resolve(results);
  21. return;
  22. }
  23. promises.forEach((promise, index) => {
  24. MyPromise.resolve(promise).then(
  25. value => {
  26. results[index] = value;
  27. completed++;
  28. if (completed === promises.length) {
  29. resolve(results);
  30. }
  31. },
  32. reject
  33. );
  34. });
  35. });
  36. }
  37. // ... 前述then方法代码 ...
  38. }

五、面试应对策略

  1. 分阶段实现

    • 第一阶段:实现基础状态管理
    • 第二阶段:添加then方法
    • 第三阶段:完善resolvePromise
  2. 调试技巧

    1. // 使用console.log跟踪状态变化
    2. new MyPromise(resolve => {
    3. console.log('executor执行');
    4. resolve(1);
    5. }).then(v => {
    6. console.log('then回调', v);
    7. return v * 2;
    8. }).then(console.log);
  3. 常见陷阱

    • 同步调用resolve时忘记异步执行回调
    • 忽略then方法的参数默认值处理
    • 未实现值穿透逻辑

六、扩展实现建议

  1. 添加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. }
  2. 实现Promise.race

    1. static race(promises) {
    2. return new MyPromise((resolve, reject) => {
    3. promises.forEach(promise => {
    4. MyPromise.resolve(promise).then(resolve, reject);
    5. });
    6. });
    7. }

七、实践验证方法

  1. 使用Promise/A+测试套件

    1. npm install promises-aplus-tests -g
    2. promises-aplus-tests your-promise-impl.js
  2. 手动测试用例
    ```javascript
    // 测试用例1:基础链式调用
    new MyPromise(resolve => resolve(1))
    .then(v => v * 2)
    .then(v => {
    console.assert(v === 2, ‘链式调用失败’);
    });

// 测试用例2:异常处理
new MyPromise((_, reject) => reject(‘error’))
.catch(err => {
console.assert(err === ‘error’, ‘异常捕获失败’);
});
```

通过系统实现Promise,开发者不仅能深入理解异步编程原理,更能掌握事件循环、闭包、状态机等核心前端概念。建议结合ES6类语法和模块化开发,将实现封装为可复用的工具库,这既是面试的加分项,也是提升代码质量的实践途径。

相关文章推荐

发表评论