logo

『JS手写』手写 Promise 跨年特辑:从原理到实现全解析

作者:谁偷走了我的奶酪2025.09.19 12:47浏览量:1

简介:本文深入解析如何手写实现符合Promise/A+规范的Promise类,包含状态管理、链式调用、异步调度等核心机制,提供可运行的完整代码和跨浏览器兼容方案。

『JS手写』手写 Promise 跨年特辑:从原理到实现全解析

一、为什么需要手写Promise?

在ES6正式将Promise纳入语言标准前,前端异步编程长期处于”回调地狱”的混乱状态。即使现在,理解Promise内部机制仍是深入掌握JavaScript异步编程的关键。手写Promise不仅能加深对Promise/A+规范的理解,更能培养解决复杂异步问题的能力,这在面试和源码级开发中具有重要价值。

1.1 历史背景

2015年ES6发布前,前端生态存在多种Promise实现:

  • jQuery的Deferred对象
  • Q.js库
  • Bluebird等高性能实现
    这些实现虽功能相似,但存在行为差异。Promise/A+规范的出现统一了社区标准。

1.2 核心价值

手写实现可带来:

  • 深入理解thenable对象机制
  • 掌握微任务调度原理
  • 理解链式调用的实现技巧
  • 培养规范意识(符合Promise/A+测试套件)

二、Promise核心机制解析

2.1 状态机模型

Promise必须实现三种状态转换:

  1. const PENDING = 'pending';
  2. const FULFILLED = 'fulfilled';
  3. const REJECTED = 'rejected';
  4. class MyPromise {
  5. constructor(executor) {
  6. this.state = PENDING;
  7. this.value = undefined;
  8. this.reason = undefined;
  9. this.onFulfilledCallbacks = [];
  10. this.onRejectedCallbacks = [];
  11. }
  12. }

状态转换规则:

  1. 只能从pending转为fulfilled/rejected
  2. 一旦转换不可逆
  3. 转换后需执行对应回调队列

2.2 异步调度策略

实现异步的关键在于微任务调度:

  1. // 兼容方案:优先使用微任务
  2. const resolvePromise = (promise, x, resolve, reject) => {
  3. // 使用MutationObserver或setTimeout模拟微任务
  4. if (typeof Promise !== 'undefined' && Promise.resolve) {
  5. Promise.resolve().then(() => executeCallback(x, resolve, reject));
  6. } else {
  7. setTimeout(() => executeCallback(x, resolve, reject), 0);
  8. }
  9. };

2.3 then方法实现

then方法是Promise的核心,需处理:

  1. 值穿透(连续then调用)
  2. 异步回调执行
  3. 链式调用支持

    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(() => handleCallback(onFulfilled, this.value, resolve, reject), 0);
    8. } else if (this.state === REJECTED) {
    9. setTimeout(() => handleCallback(onRejected, this.reason, resolve, reject), 0);
    10. } else {
    11. this.onFulfilledCallbacks.push(value =>
    12. handleCallback(onFulfilled, value, resolve, reject));
    13. this.onRejectedCallbacks.push(reason =>
    14. handleCallback(onRejected, reason, resolve, reject));
    15. }
    16. });
    17. return promise2;
    18. }

三、完整实现与规范兼容

3.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 (value instanceof MyPromise) {
  10. return value.then(resolve, reject);
  11. }
  12. if (this.state === PENDING) {
  13. this.state = FULFILLED;
  14. this.value = value;
  15. this.onFulfilledCallbacks.forEach(fn => fn());
  16. }
  17. };
  18. const reject = reason => {
  19. if (this.state === PENDING) {
  20. this.state = REJECTED;
  21. this.reason = reason;
  22. this.onRejectedCallbacks.forEach(fn => fn());
  23. }
  24. };
  25. try {
  26. executor(resolve, reject);
  27. } catch (err) {
  28. reject(err);
  29. }
  30. }
  31. // ...then方法实现(同上)
  32. }

3.2 静态方法实现

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

3.3 跨浏览器兼容方案

  1. 微任务模拟

    1. const asyncFlush = (() => {
    2. if (typeof Promise !== 'undefined') {
    3. return () => Promise.resolve().then(flush);
    4. } else if (typeof MutationObserver !== 'undefined') {
    5. let queue = [];
    6. const node = document.createTextNode('');
    7. new MutationObserver(() => {
    8. while (queue.length) queue.shift()();
    9. }).observe(node, { characterData: true });
    10. return callback => {
    11. queue.push(callback);
    12. node.data = Math.random();
    13. };
    14. } else {
    15. return callback => setTimeout(callback, 0);
    16. }
    17. })();
  2. Object.prototype污染防护
    ```javascript
    const hasOwnProperty = Object.prototype.hasOwnProperty;
    function isObject(obj) {
    return obj !== null && typeof obj === ‘object’;
    }

function getThen(x) {
const then = isObject(x) && hasOwnProperty.call(x, ‘then’) ? x.then : null;
if (typeof then === ‘function’) return then;
return null;
}

  1. ## 四、进阶实现技巧
  2. ### 4.1 链式调用优化
  3. ```javascript
  4. then(onFulfilled, onRejected) {
  5. const promise2 = new MyPromise((resolve, reject) => {
  6. const handleFulfilled = value => {
  7. try {
  8. if (typeof onFulfilled !== 'function') {
  9. resolve(value);
  10. } else {
  11. const x = onFulfilled(value);
  12. resolvePromise(promise2, x, resolve, reject);
  13. }
  14. } catch (e) {
  15. reject(e);
  16. }
  17. };
  18. // 类似实现handleRejected...
  19. });
  20. return promise2;
  21. }

4.2 性能优化策略

  1. 回调合并:使用单个定时器处理所有回调
  2. 错误边界:try-catch包裹所有回调执行
  3. 值缓存:避免重复计算thenable值

五、测试验证方案

5.1 基础测试用例

  1. // 状态转换测试
  2. const promise = new MyPromise((resolve) => {
  3. setTimeout(() => resolve(1), 100);
  4. });
  5. promise.then(value => {
  6. console.log(value); // 应输出1
  7. });
  8. // 链式调用测试
  9. MyPromise.resolve(1)
  10. .then(value => value + 1)
  11. .then(value => value * 2)
  12. .then(value => {
  13. console.log(value); // 应输出4
  14. });

5.2 规范兼容测试

使用promises-aplus-tests进行验证:

  1. 安装测试工具:npm install promises-aplus-tests -g
  2. 创建适配器:
    1. const adapter = {
    2. resolved: MyPromise.resolve,
    3. rejected: MyPromise.reject,
    4. deferred: () => {
    5. const result = {};
    6. result.promise = new MyPromise((resolve, reject) => {
    7. result.resolve = resolve;
    8. result.reject = reject;
    9. });
    10. return result;
    11. }
    12. };
  3. 运行测试:promises-aplus-tests adapter.js

六、实际应用建议

  1. 学习阶段:先理解核心逻辑,再逐步完善边缘情况
  2. 生产环境:优先使用原生Promise或成熟库(如Bluebird)
  3. 调试技巧

    • 使用Promise.prototype.finally追踪流程
    • 通过console.log标记状态变化
    • 利用开发者工具的Async堆栈追踪
  4. 性能优化

    • 避免在then回调中创建新Promise
    • 合理使用Promise.all处理并行请求
    • 对可取消操作实现AbortController

七、总结与展望

手写Promise实现是理解JavaScript异步编程的终极试炼。通过实现过程,开发者不仅能掌握状态管理、异步调度等核心概念,更能培养规范意识和工程化思维。随着ES2023引入Promise.try等新特性,Promise生态仍在持续演进,但底层原理始终是理解新特性的基石。

建议读者在完成基础实现后,尝试:

  1. 实现Promise.allSettled
  2. 添加取消功能
  3. 研究async/await的编译原理
  4. 探索Generator函数与Promise的关系

通过持续实践和思考,开发者将构建起完整的异步编程知识体系,为处理复杂前端架构打下坚实基础。

相关文章推荐

发表评论