手写 Promise:深入理解 JavaScript 异步编程的核心

引言

在现代 JavaScript 开发中,Promise是处理异步操作的基石。从回调地狱道 async/await 的优雅,Promise 扮演了关键角色。但是,你真的理解 Promise 是如何工作的吗?

通过手写一个符合 Promise/A+ 规范的 Promise, 我们不仅能深入理解异步编程的机制,还能掌握 JavaScript 中许多核心概念: 微任务、事件循环、状态机等。更重要的是,这在面试中是必考的高频题目!

本文将带你从零实现一个完整的 Promise,并深入探讨其设计原理和实现细节。

一、Promise的核心概念

1.1 Promise的三种状态
javascript 复制代码
// Promise的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
1.2 Promise/A+规范要点
  1. Promise必须处于三种状态之一: pending、fulfilled、rejected
  2. 状态一旦改变,就不能再次改变(不可逆性)
  3. then方法必须返回一个新的 Promise
  4. then方法可以被同一个Promise调用多次
  5. 值穿透(value penetration)
  6. 错误冒泡(error bubbling)

二、基础Promise的实现

2.1 基础骨架
javascript 复制代码
class MyPromise {
  constructor(executor) {
    // 初始状态
    this.state = "PENDING";
    this.value = undefined;
    this.reason = undefined;

    // 用于存储 then 方法的回调函数
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    // 执行器函数
    const resolve = (value) => {
      if (this.state === "PENDING") {
        this.state = "FULFILLED";
        this.value = value;
        // 执行所有成功回调
        this.onFulfilledCallbacks.forEach((callback) => callback());
      }
    };

    const reject = (reason) => {
      if (this.state === "PENDING") {
        this.state = "REJECTED";
        this.reason = reason;
        // 执行所有失败回调
        this.onRejectedCallbacks.forEach((callback) => callback());
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    // 基础实现,后续会完善
    if (this.state === "FULFILLED") {
      onFulfilled(this.value);
    }
    if (this.state === "REJECTED") {
      onRejected(this.reason);
    }
    if (this.state === "PENDING") {
      this.onFulfilledCallbacks.push(() => onFulfilled(this.value));
      this.onRejectedCallbacks.push(() => onRejected(this.reason));
    }
  }
}

// 基础使用
const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
  }, 1000);
});

promise.then(
  (value) => console.log("成功:", value),
  (reason) => console.log("失败:", reason)
);
// 成功: success

三、完整Promise实现(符合Promise/A+规范)

3.1 完整的MyPromise类
javascript 复制代码
// 定义Promise的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

// 判断是否为函数
const isFunction = (fn) => typeof fn === "function";

// 处理then方法中的回调,确保总是异步执行
const nextTick = (fn) => {
  // 使用微任务(优先)或宏任务
  if (typeof process !== "undefined" && process.nextTick) {
    process.nextTick(fn); // NodeJS环境
  } else if (typeof MutationObserver !== "undefined") {
    // 浏览器环境使用 MutationObserver 模拟微任务
    let counter = 1;
    const observer = new MutationObserver(fn);
    const textNode = document.createTextNode(String(counter));
    observer.observe(textNode, { characterData: true });
    counter = (counter + 1) % 2;
    textNode.data = String(counter);
  } else {
    setTimeout(fn, 0); // 降级到宏任务
  }
};

// 处理Promise解析过程(Promise Resolution Procedure)
function resolvePromise(promise, x, resolve, reject) {
  // 防止循环引用
  if (promise === x) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }

  // 标记是否已调用, 防止多次调用
  let called = false;

  if (x !== null && (typeof x === "object" || typeof x === "function")) {
    try {
      // 获取 then 方法
      const then = x.then;

      // 如果then是函数,则认为x是一个thenable对象
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            // 递归解析,直到得到非Promise值
            resolvePromise(promise, x, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        // 如果 then 不是函数,直接resolve x
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    // 如果x不是对象或函数, 直接resolve x
    resolve(x);
  }
}

class MyPromise {
  constructor(executor) {
    // 状态
    this.state = PENDING;
    // 成功值
    this.value = undefined;
    // 失败值
    this.reason = undefined;

    // 存储回调函数
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    // 定义resolve函数
    const resolve = (value) => {
      // 只有 pending 状态可以改变
      if (this.state !== PENDING) return;

      // 如果value是一个Promise, 则等待该Promise完成
      if (value instanceof MyPromise) {
        value.then(resolve, reject);
        return;
      }

      // 异步执行
      nextTick(() => {
        if (this.state !== PENDING) return;
        this.state = FULFILLED;
        this.value = value;
        // 执行所有成功回调
        this.onFulfilledCallbacks.forEach((callback) => callback());
      });
    };

    // 定义reject函数
    const reject = (reason) => {
      if (this.state !== PENDING) return;

      nextTick(() => {
        if (this.state !== PENDING) return;
        this.state = REJECTED;
        this.reason = reason;
        // 执行所有失败回调
        this.onRejectedCallbacks.forEach((callback) => callback());
      });
    };

    // 执行executor
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  // then方法
  then(onFulfilled, onRejected) {
    // 值穿透: 如果参数不是函数,则创建一个函数直接传递值
    onFulfilled = isFunction(onFulfilled) ? onFulfilled : (value) => value;
    onRejected = isFunction(onRejected)
      ? onRejected
      : (reason) => {
          throw reason;
        };

    // 创建一个新的Promise
    const promise2 = new MyPromise((resolve, reject) => {
      const handleFulfilled = () => {
        nextTick(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

      const handleRejected = () => {
        nextTick(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

      // 根据当前状态执行不同的逻辑
      if (this.state === FULFILLED) {
        handleFulfilled();
      } else if (this.state === REJECTED) {
        handleRejected();
      } else {
        // PENDING状态,将回调存储起来
        this.onFulfilledCallbacks.push(handleFulfilled);
        this.onRejectedCallbacks.push(handleRejected);
      }
    });

    return promise2;
  }

  // catch方法
  catch(onRejected) {
    return this.then(null, onRejected);
  }

  // finally方法
  finally(onFinally) {
    return this.then(
      (value) => MyPromise.resolve(onFinally()).then(() => value),
      (reason) =>
        MyPromise.resolve(onFinally()).then(() => {
          throw reason;
        })
    );
  }

  // 静态方法: resolve
  static resolve(value) {
    // 如果已经是Promise实例,直接返回
    if (value instanceof MyPromise) {
      return value;
    }

    // 如果是thenable对象
    if (value && typeof value.then === "function") {
      return new MyPromise(value.then);
    }

    // 其他情况
    return new MyPromise((resolve) => resolve(value));
  }

  // 静态方法: reject
  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason));
  }

  // 静态方法: all
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument must be an array"));
      }

      const results = [];
      let completedCount = 0;

      if (promises.length === 0) {
        return resolve(results);
      }

      const processResult = (index, value) => {
        results[index] = value;
        completedCount++;

        if (completedCount === promises.length) {
          resolve(results);
        }
      };

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          (value) => processResult(index, value),
          reject // 任何一个失败,整个Promise就失败
        );
      });
    });
  }

  // 静态方法: race
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument must be an array"));
      }

      promises.forEach((promise) => {
        MyPromise.resolve(promise).then(resolve, reject);
      });
    });
  }

  // 静态方法: allSettled
  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument must be an array"));
      }

      const results = [];
      let completedCount = 0;

      if (promises.length === 0) {
        return resolve(results);
      }

      const processResult = (index, status, value) => {
        results[index] = {
          status,
          [status === "fulfilled" ? "value" : "reason"]: value,
        };
        completedCount++;

        if (completedCount === promises.length) {
          resolve(results);
        }
      };

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          (value) => processResult(index, "fulfilled", value),
          (reason) => processResult(index, "rejected", reason)
        );
      });
    });
  }

  // 静态方法: any
  static any(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument must be an array"));
      }

      const errors = [];
      let rejectedCount = 0;

      if (promises.length === 0) {
        return reject(new AggregateError([], "All promises were rejected"));
      }

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(resolve, (reason) => {
          errors[index] = reason;
          rejectedCount++;

          if (rejectedCount === promises.length) {
            reject(new AggregateError(errors, "All promises were rejected"));
          }
        });
      });
    });
  }
}

四、测试用例

4.1 基础功能测试
javascript 复制代码
console.log("=== 基础功能测试 ===");

// 测试1: 基础异步操作
const p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => resolve("success"), 100);
});

p1.then((value) => {
  console.log("测试1:", value); // success
});

// 测试2: 链式调用
const p2 = new MyPromise((resolve) => resolve(1));

p2.then((value) => {
  console.log("测试2-1:", value); // 1
  return value + 1;
})
  .then((value) => {
    console.log("测试2-2:", value); // 2
    return new MyPromise((resolve) => resolve(value + 1));
  })
  .then((value) => {
    console.log("测试2-3:", value); // 3
  });

// 测试3: 错误处理
const p3 = new MyPromise((resolve, reject) => {
  reject("error");
});

p3.then(
  (value) => console.log("不会执行"),
  (reason) => console.log("测试3:", reason) // error
).catch((error) => {
  console.log("测试3-catch:", error);
});

// 测试4: 值穿透
const p4 = new MyPromise((resolve) => resolve("value"));

p4.then()
  .then()
  .then((value) => console.log("测试4:", value)); // value
4.2 静态方法测试
javascript 复制代码
console.log('\n=== 静态方法测试 ===');

// Promise.all
MyPromise.all([
    MyPromise.resolve(1),
    MyPromise.resolve(2),
    new MyPromise(resolve => setTimeout(() => resolve(3), 100))
]).then(values => {
    console.log('Promise.all:', values); // [1, 2, 3]
});

// Promise.race
MyPromise.race([
    new MyPromise(resolve => setTimeout(() => resolve('fast'), 50)),
    new MyPromise(resolve => setTimeout(() => resolve('slow'), 100))
]).then(value => {
    console.log('Promise.race:', value); // fast
});

// Promise.allSettled
MyPromise.allSettled([
    MyPromise.resolve(1),
    MyPromise.reject('error'),
    MyPromise.resolve(3)
]).then(results => {
    console.log('Promise.allSettled:', results);
    // [
    //   { status: 'fulfilled', value: 1 },
    //   { status: 'rejected', reason: 'error' },
    //   { status: 'fulfilled', value: 3 }
    // ]
});
4.3 符合Promise/A+规范的测试
javascript 复制代码
console.log("\n=== Promise/A+ 规范测试 ===");

// 测试 then 方法被多次调用
const p = new MyPromise((resolve) => {
  setTimeout(() => resolve("multi-call"), 50);
});

p.then((value) => console.log("第一次调用:", value));
p.then((value) => console.log("第二次调用:", value));
p.then((value) => console.log("第三次调用:", value));

// 测试 then 方法的异步特性
console.log("同步代码开始");
new MyPromise((resolve) => resolve("async")).then((value) => {
  console.log("异步回调:", value);
});
console.log("同步代码结束");
// 输出顺序:
// 同步代码开始
// 同步代码结束
// 异步回调: async

// 测试循环引用
const p5 = new MyPromise((resolve) => resolve(1));
const p6 = p5.then(() => p6);

p6.then(
  () => console.log("不会执行"),
  (reason) => console.log("循环引用错误:", reason instanceof TypeError) // true
);
4.4 复杂场景测试
javascript 复制代码
console.log("\n=== 复杂场景测试 ===");

// 场景1: 混合 Promise 和普通值
MyPromise.all([
  1, // 普通值
  MyPromise.resolve(2),
  new MyPromise((resolve) => setTimeout(() => resolve(3), 100)),
]).then((values) => {
  console.log("混合类型:", values); // [1, 2, 3]
});

// 场景2: 嵌套 Promise
const nested = new MyPromise((resolve) => {
  resolve(
    new MyPromise((innerResolve) => {
      innerResolve("deep value");
    })
  );
});

nested.then((value) => {
  console.log("嵌套 Promise:", value); // deep value
});

// 场景3: thenable 对象
const thenable = {
  then: function (resolve, reject) {
    setTimeout(() => resolve("thenable"), 50);
  },
};

MyPromise.resolve(thenable).then((value) => {
  console.log("Thenable 对象:", value); // thenable
});

// 场景4: finally 方法
new MyPromise((resolve) => resolve("finally test"))
  .finally(() => {
    console.log("finally 执行了");
    return MyPromise.resolve("延迟");
  })
  .then((value) => {
    console.log("finally 后的值:", value); // finally test
  });

五、Promise的核心原理分析

5.1 状态机的实现

Promise本质上是一个状态机, 包含三种状态:

  • pending: 初始状态
  • fulfilled: 操作成功完成
  • rejected: 操作失败

状态转换是不可逆的,这是通过内部状态变量和控制逻辑实现的。

5.2 异步调度的实现

原生的Promise使用微任务(microtask)队列, 而我们使用nextTick函数来模拟:

  • NodeJS环境: process.nextTick
  • 浏览器环境: MutationObserversetTimeout 这种异步调度确保了:
  1. then方法的回调总是在当前执行栈结束后执行
  2. 符合Promise/A+规范中的异步要求
5.3 链式调用的实现

链式调用的关键在于then方法总是返回一个新的Promise。这通过以下步骤实现:

  1. 创建新的Promise(promise2)
  2. 在适当的时机(状态改变时)执行回调
  3. 通过resolvePromise处理回调的返回值
5.4 Promise解析过程

resolvePromise函数是Promise实现中最复杂的部分,它处理了:

  1. 循环引用检测
  2. thenable对象的处理
  3. 递归解析
  4. 防止多次调用

六、常见面试题实现

6.1 实现Promise.all
javascript 复制代码
Promise.myAll = function (promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError("Argument must be an array"));
    }

    let results = [];
    let count = 0;

    if (promises.length === 0) {
      return resolve(results);
    }

    promises.forEach((promise, index) => {
      Promise.resolve(promise).then(
        (value) => {
          results[index] = value;
          count++;

          if (count === promises.length) {
            resolve(results);
          }
        },
        reject // 任何一个失败就整体失败
      );
    });
  });
};
6.2 实现Promise.race
javascript 复制代码
Promise.myRace = function (promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError("Argument must be an array"));
    }

    promises.forEach((promise) => {
      Promise.resolve(promise).then(resolve, reject);
    });
  });
};
6.3 实现Promise超时控制
javascript 复制代码
Promise.withTimeout = function (promise, timeout, timeoutMessage = "Timeout") {
  return Promise.race([
    promise,
    new Promise((_, reject) => {
      setTimeout(() => reject(new Error(timeoutMessage)), timeout);
    }),
  ]);
};
// 使用示例
const fetchData = new Promise((resolve) => {
  setTimeout(() => resolve("data"), 2000);
});

Promise.withTimeout(fetchData, 1000, "Request timeout")
  .then((data) => console.log("成功:", data))
  .catch((error) => console.log("失败:", error.message));
// 失败: Request timeout
6.4 实现Promise重试机制
javascript 复制代码
Promise.retry = function (fn, times, delay = 1000) {
  return new Promise((resolve, reject) => {
    const attempt = (remaining) => {
      fn()
        .then(resolve)
        .catch((error) => {
          if (remaining <= 1) {
            reject(error);
          } else {
            console.log(`重试,剩余次数: ${remaining - 1}`);
            setTimeout(() => attempt(remaining - 1), delay);
          }
        });
    };

    attempt(times);
  });
};

// 使用示例
let attemptCount = 0;
const unstableOperation = () => {
  attemptCount++;
  console.log(`第 ${attemptCount} 次尝试`);
  return new Promise((resolve, reject) => {
    if (Math.random() > 0.3) {
      reject(new Error("随机失败"));
    } else {
      resolve("成功!");
    }
  });
};

Promise.retry(unstableOperation, 5, 500)
  .then((result) => console.log("最终结果:", result))
  .catch((error) => console.log("最终失败:", error.message));

七、性能优化和注意事项

7.1 内存泄漏预防

Promise实现中要注意:

  1. 及时清理回调数组: 执行完回调后可以清空数组
  2. 避免循环引用: 在resolvePromise中检测
  3. 使用WeakMap存储元数据(可选)
7.2 错误处理最佳实践
javascript 复制代码
// 不好的做法
promise.then(
    successHandler,
    errorHandler // 无法捕获 successHandler 中的错误
);

// 好的做法
promise
    .then(successHandler)
    .catch(errorHandler); // 可以捕获链中所有错误

// 或者在 then 中同时处理
promise.then(
    value => {
        try {
            return successHandler(value);
        } catch (error) {
            return Promise.reject(error);
        }
    },
    errorHandler
);
7.3 批量处理优化
javascript 复制代码
// 优化大量 Promise 的处理
class PromiseBatch {
    constructor(concurrency = 5) {
        this.concurrency = concurrency;
        this.queue = [];
        this.running = 0;
    }
    
    add(promiseFactory) {
        return new Promise((resolve, reject) => {
            this.queue.push({
                promiseFactory,
                resolve,
                reject
            });
            this.run();
        });
    }
    
    run() {
        while (this.running < this.concurrency && this.queue.length) {
            const task = this.queue.shift();
            this.running++;
            
            task.promiseFactory()
                .then(task.resolve)
                .catch(task.reject)
                .finally(() => {
                    this.running--;
                    this.run();
                });
        }
    }
}

// 使用示例
const batch = new PromiseBatch(3);
const results = [];

for (let i = 0; i < 10; i++) {
    batch.add(() => {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log(`任务 ${i} 完成`);
                resolve(i);
            }, Math.random() * 1000);
        });
    }).then(result => results.push(result));
}

八、现代JavaScript中的Promise相关特性

8.1 async/await的实现原理

async/await本质上是 Generator 函数和Promise的语法糖:

javascript 复制代码
// async/await的近似实现
function asyncToGenerator(generatorFunc) {
  return function (...args) {
    const generator = generatorFunc.apply(this, args);

    return new Promise((resolve, reject) => {
      function step(key, arg) {
        let result;
        try {
          result = generator[key](arg);
        } catch (error) {
          return reject(error);
        }

        const { value, done } = result;

        if (done) {
          return resolve(value);
        } else {
          return Promise.resolve(value).then(
            (val) => step("next", val),
            (err) => step("throw", err)
          );
        }
      }

      step("next");
    });
  };
}

// 使用示例
const asyncFunc = asyncToGenerator(function* () {
  const result1 = yield Promise.resolve(1);
  const result2 = yield Promise.resolve(result1 + 1);
  return result2 + 1;
});

asyncFunc().then((value) => console.log(value)); // 3
8.2 Promise与生成器的配合
javascript 复制代码
// 自动执行 Generator 函数
function runGenerator(genFunc) {
  const generator = genFunc();

  function handle(result) {
    if (result.done) return Promise.resolve(result.value);

    return Promise.resolve(result.value)
      .then((res) => handle(generator.next(res)))
      .catch((err) => handle(generator.throw(err)));
  }

  try {
    return handle(generator.next());
  } catch (error) {
    return Promise.reject(error);
  }
}

// 使用示例
runGenerator(function* () {
  const a = yield Promise.resolve(1);
  const b = yield Promise.resolve(2);
  return a + b;
}).then((result) => console.log(result)); // 3

九、总结与最佳实践

9.1 核心要点总结
  1. Promise是状态机: 三种状态,不可逆转换
  2. then方法返回新Promise: 实现链式调用的关键
  3. 异步执行: 回调总是在当前执行栈结束后执行
  4. 错误冒泡: 错误会沿着Promise链传递
  5. 值穿透: 非函数参数会被忽略
9.2 手写Promise的价值
  1. 深入理解异步机制: 理解微任务、事件循环
  2. 掌握设计模式: 状态机、观察者模式
  3. 提升代码质量: 更好的错误处理、代码组织
  4. 面试优势: 高频面试题,展示扎实的基础
9.3 实际开发建议
  1. 总是返回Promise: 确保函数的一致性
  2. 避免嵌套: 使用链式调用或async/await
  3. 处理所有错误: 使用catchtry-catch
  4. 合理使用静态方法: allraceallSettled
9.4 进一步学习方向
  1. 源码阅读: 阅读原生Promise的源码实现
  2. 异步编程模式: 学习RxJSasync/await
  3. 性能优化: 学习Promise的性能优化技巧
  4. 框架集成: 学习如何在框架中合理使用Promise

结语

通过手写Promise, 我们不仅掌握了一个重要的JavaScript特性,更重要的是理解了异步编程的核心思想。Promise的设计体现了许多优秀的软件设计原: 单一职责、开闭原则、依赖倒置等。

在实际开发中,我们可能不会经常需要自己实现Promise,但理解其原理能让我们更好地使用它,避免常见的陷阱,写出更健壮的异步代码。

记住,Promise不是终点,而是异步编程的起点。随着JavaScript的发展,我们有了async/awaitGeneratorObservable等更多强大的工具。但Promise作为基础,它的设计思想和实现原理永远值得我们深入学习和理解。

延伸阅读

希望这篇文章能帮助你深入理解Promise!如果有任何问题或建议,欢迎讨论交流。

相关推荐
铅笔侠_小龙虾1 小时前
Vue 学习目录
前端·vue.js·学习
悟能不能悟1 小时前
vue的history和hash模式有什么不一样
前端·vue.js
晓庆的故事簿1 小时前
前端的浏览器地址传参
前端
*星星之火*1 小时前
【大白话 AI 答疑】第6篇 大模型指令微调:instruction/input/output核心解析及案例
服务器·前端·人工智能
北杳同学2 小时前
前端一些用得上的有意思网站
前端·javascript·vue.js·学习
张3蜂2 小时前
CSRF Token:网络应用安全的关键防线——深度解析与实战指南
前端·安全·csrf
IT_陈寒2 小时前
Redis 性能骤降50%?这5个隐藏配置陷阱你可能从未注意过
前端·人工智能·后端
躺着听Jay2 小时前
【1267 - Illegal mix of collations 】mysql报错解决记录
java·linux·前端
Dragon Wu2 小时前
ReactNative Expo 使用总结(基础)
javascript·react native·react.js