logo

手写Promise与Promises/A+规范实战:从零实现到测试通过

作者:da吃一鲸8862025.09.19 12:47浏览量:0

简介:本文通过手写Promise核心逻辑并实现Promises/A+测试规范,深入解析异步编程的核心机制,提供可运行的代码实现与测试方案,帮助开发者掌握Promise底层原理。

手写Promise与Promises/A+规范实战:从零实现到测试通过

一、为什么需要手写Promise?

Promise作为JavaScript异步编程的核心解决方案,其设计原理涉及状态管理、异步链式调用、错误处理等关键机制。尽管现代开发中直接使用原生Promise或async/await已足够,但手写Promise实现具有以下价值:

  1. 深度理解异步机制:通过实现状态机(pending→fulfilled/rejected)、微任务队列等底层逻辑,掌握事件循环与异步调度的本质
  2. 符合工业标准:Promises/A+规范定义了Promise的最小行为准则,实现该规范可确保与现有生态(如async库、测试工具)的兼容性
  3. 定制化能力:在特定场景下(如性能优化、特殊错误处理),自定义Promise实现可能比原生实现更高效

二、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. 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. }

关键点解析

  • 状态机设计:严格区分pending/fulfilled/rejected三种状态,且状态不可逆
  • 异步回调队列:使用数组存储then方法注册的回调,在状态变更时批量执行
  • 错误捕获:在executor执行时包裹try-catch,确保同步错误也能被捕获

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

设计要点

  • 异步调度:使用setTimeout将回调推入微任务队列(实际实现可用MutationObserver或MessageChannel模拟)
  • 返回值处理:通过resolvePromise函数处理then返回值的各种情况(Promise/值/异常)
  • 链式调用:每次then都返回新Promise,形成调用链

3. resolvePromise规范实现

  1. function resolvePromise(promise2, x, resolve, reject) {
  2. // 防止循环引用
  3. if (promise2 === x) {
  4. return reject(new TypeError('Chaining cycle detected for promise'));
  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. }

规范要点

  • 类型判断:严格处理x为对象/函数的情况
  • thenable对象支持:兼容非Promise但具有then方法的对象
  • 一次调用保证:通过called标志防止多次resolve/reject
  • 异常捕获:对then方法调用进行try-catch包装

三、Promises/A+测试实现指南

1. 测试环境搭建

  1. 安装测试工具:

    1. npm install promises-aplus-tests -D
  2. 创建适配接口文件(my-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. ### 2. 运行测试命令
  2. ```bash
  3. npx promises-aplus-tests my-promise-adapter.js

3. 关键测试项解析

  1. 状态变更测试

    • 验证只能从pending变为fulfilled/rejected
    • 验证状态变更后不可逆
  2. 链式调用测试

    • 验证then方法返回新Promise
    • 验证返回值穿透(非Promise值直接传递)
  3. 异常处理测试

    • 验证同步错误捕获
    • 验证then回调中的异常传递
  4. Promise解析测试

    • 验证thenable对象的正确处理
    • 验证循环引用检测

四、性能优化与扩展实现

1. 微任务调度优化

原生Promise使用微任务队列,可通过以下方式模拟:

  1. // 使用MutationObserver实现微任务
  2. function asyncSchedule(fn) {
  3. const observer = new MutationObserver(fn);
  4. const node = document.createElement('div');
  5. observer.observe(node, { attributes: true });
  6. node.setAttribute('data', 'trigger');
  7. }
  8. // 或使用MessageChannel
  9. const channel = new MessageChannel();
  10. channel.port2.onmessage = fn;
  11. channel.port1.postMessage(null);

2. 静态方法实现

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

五、实际开发中的注意事项

  1. 与原生Promise的互操作

    • 确保自定义Promise能与fetch、async/await等原生API协同工作
    • 处理第三方库返回的Promise时保持兼容性
  2. 调试支持

    • 添加Promise.prototype[Symbol.toStringTag] = ‘MyPromise’便于调试
    • 实现catch方法的便捷调用
  3. 性能基准测试

    • 使用JSPerf对比自定义实现与原生Promise的性能差异
    • 重点关注then方法调用和状态变更的耗时

六、完整实现代码示例

完整实现代码仓库包含:

  1. 核心Promise类实现
  2. 所有静态方法
  3. 测试适配文件
  4. 性能测试脚本

通过本文的实现,开发者不仅能深入理解Promise的工作原理,更能掌握如何实现符合工业标准的异步解决方案。这种从底层到上层的完整实践,对于提升JavaScript异步编程能力具有重要价值。

相关文章推荐

发表评论