logo

手写Promise全解析:从基础实现到Promises/A+测试通关

作者:蛮不讲李2025.09.19 12:56浏览量:0

简介:本文深入解析如何手写实现Promise并满足Promises/A+规范,通过代码示例和测试用例详解核心机制,帮助开发者掌握异步编程的底层原理。

手写Promise全解析:从基础实现到Promises/A+测试通关

一、Promise核心机制解析

Promise作为现代JavaScript异步编程的核心,其设计遵循”状态机”理论。一个Promise对象必然处于以下三种状态之一:

  • Pending(待定):初始状态,可迁移至Fulfilled或Rejected
  • Fulfilled(已兑现):表示操作成功完成
  • Rejected(已拒绝):表示操作失败

状态迁移具有不可逆性,这是实现Promise时必须严格遵守的规范。以文件读取操作为例,当fs.readFile成功完成时,Promise应进入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的核心机制:状态管理、异步回调队列和错误捕获。值得注意的是,resolve和reject函数必须进行状态检查,防止重复状态变更。

二、then方法实现与链式调用

then方法是Promise实现中最复杂的部分,需要处理三种关键场景:

  1. 立即返回新Promise:实现链式调用的基础
  2. 回调函数异步执行:符合规范要求的微任务特性
  3. 值穿透机制:处理非函数回调的情况

2.1 完整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. }

2.2 关键辅助函数:resolvePromise

这个函数处理Promise链式调用中的值传递,需要遵循以下规则:

  1. 如果x是Promise实例,则等待其决议
  2. 如果x是对象或函数,尝试解析其then方法
  3. 否则直接resolve(x)
  1. function resolvePromise(promise2, x, resolve, reject) {
  2. if (promise2 === x) {
  3. return reject(new TypeError('Chaining cycle detected'));
  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. }

三、Promises/A+规范测试实现

Promises/A+测试套件包含288个测试用例,验证实现是否符合规范。测试环境搭建步骤如下:

3.1 测试环境配置

  1. 安装测试运行器:

    1. npm install promises-aplus-tests -D
  2. 创建适配器文件promise-adapter.js
    ```javascript
    const MyPromise = require(‘./my-promise’);

module.exports = {
deferred: () => {
let resolve, reject;
const promise = new MyPromise((res, rej) => {
resolve = res;
reject = rej;
});
return {
promise,
resolve,
reject
};
}
};

  1. 3. 运行测试:
  2. ```bash
  3. npx promises-aplus-tests promise-adapter.js

3.2 关键测试点解析

测试套件主要验证以下方面:

  1. 状态变更:确保状态只能从pending变为fulfilled/rejected
  2. 回调执行:验证then方法在状态变更后正确执行回调
  3. 链式调用:测试Promise链的传递和值穿透
  4. 异常处理:验证同步和异步错误的捕获机制

典型测试用例示例:

  1. // 测试用例:then方法返回新Promise
  2. it('should return a new promise', (done) => {
  3. const deferred = adapter.deferred();
  4. const result = deferred.promise.then(() => {});
  5. assert(result instanceof Promise, 'then should return a Promise');
  6. deferred.resolve();
  7. done();
  8. });

四、高级特性实现

4.1 静态方法实现

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

4.2 微任务调度优化

实际实现中,可以使用queueMicrotaskMutationObserver来模拟微任务队列:

  1. const microtaskQueue = [];
  2. let isFlushing = false;
  3. function flushMicrotasks() {
  4. if (isFlushing) return;
  5. isFlushing = true;
  6. while (microtaskQueue.length) {
  7. const callback = microtaskQueue.shift();
  8. callback();
  9. }
  10. isFlushing = false;
  11. }
  12. // 替换setTimeout
  13. function scheduleMicrotask(callback) {
  14. microtaskQueue.push(callback);
  15. if (!isFlushing) {
  16. Promise.resolve().then(flushMicrotasks);
  17. }
  18. }

五、性能优化与最佳实践

  1. 内存管理:在状态变更后清空回调队列
  2. 错误边界:使用try-catch包裹所有同步操作
  3. 调试支持:添加Promise链跟踪信息
  4. 兼容性处理:处理thenable对象的特殊情况

优化后的resolve函数示例:

  1. const resolve = (value) => {
  2. if (this.state !== 'pending') return;
  3. // 处理thenable对象
  4. if (value !== null && (typeof value === 'object' || typeof value === 'function')) {
  5. try {
  6. const then = value.then;
  7. if (typeof then === 'function') {
  8. then.call(value, resolve.bind(this), reject.bind(this));
  9. return;
  10. }
  11. } catch (e) {
  12. reject.call(this, e);
  13. return;
  14. }
  15. }
  16. this.state = 'fulfilled';
  17. this.value = value;
  18. // 清空回调队列节省内存
  19. this.onFulfilledCallbacks = [];
  20. scheduleMicrotask(() => this.onFulfilledCallbacks.forEach(fn => fn()));
  21. };

六、总结与进阶建议

实现符合Promises/A+规范的Promise需要深入理解:

  1. 状态机的不可逆性
  2. 异步执行的调度机制
  3. 链式调用的值传递规则
  4. 异常处理的边界条件

建议开发者

  1. 优先理解规范文档,再着手实现
  2. 逐步实现功能,先保证基础then方法正确
  3. 使用测试套件验证实现
  4. 参考成熟实现(如promises-es6)学习优化技巧

完整实现代码约200行,通过测试套件验证后,可应用于生产环境中的简单场景。对于复杂需求,建议使用经过充分测试的库如Bluebird或Zoroaster。

相关文章推荐

发表评论