深入理解 JavaScript Promise 的高效实现

引言

在现代 JavaScript 开发中,Promise 是异步操作的核心工具。无论是简单的回调替代,还是复杂的异步任务管理,Promise 都提供了一种简洁、高效的方式。然而,理解其内部实现和优化点不仅有助于掌握异步编程的本质,也能提升代码的性能与可维护性。

本文将探索如何实现一个高效、功能完善的 Promise,并剖析其中的核心机制与优化细节。


Promise 的核心机制

Promise 的底层基于状态机思想,其状态具有以下特性:

  1. 三种状态

    • pending(初始状态):表示异步操作尚未完成。
    • fulfilled(已完成):表示异步操作成功完成。
    • rejected(已拒绝):表示异步操作失败。
  2. 不可逆性

    • 状态只能从 pending 转为 fulfilledrejected,一旦变更,状态将无法再次改变。
  3. 异步回调触发

    • 注册的回调函数(通过 thencatch)必须在状态变更后异步执行,符合 JavaScript 事件循环的微任务调度规则。

优化后的 Promise 实现

以下是优化版 Promise 的完整实现,涵盖了基本功能、链式调用、静态方法等,同时对性能和易用性进行了提升。

javascript 复制代码
class OptimizedPromise {
    constructor(executor) {
        this.state = "pending"; // 初始状态
        this.value = undefined; // 成功的值
        this.reason = undefined; // 失败的原因
        this.callbacks = []; // 存储回调函数

        const resolve = (value) => {
            if (this.state === "pending") {
                this.state = "fulfilled";
                this.value = value;

                queueMicrotask(() => {
                    this.callbacks.forEach(({ onFulfilled }) => {
                        if (onFulfilled) onFulfilled(this.value);
                    });
                });
            }
        };

        const reject = (reason) => {
            if (this.state === "pending") {
                this.state = "rejected";
                this.reason = reason;

                queueMicrotask(() => {
                    this.callbacks.forEach(({ onRejected }) => {
                        if (onRejected) onRejected(this.reason);
                    });
                });
            }
        };

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

    then(onFulfilled, onRejected) {
        return new OptimizedPromise((resolve, reject) => {
            const handleCallback = () => {
                try {
                    if (this.state === "fulfilled") {
                        if (onFulfilled) {
                            const result = onFulfilled(this.value);
                            resolve(result instanceof OptimizedPromise ? result : result);
                        } else {
                            resolve(this.value);
                        }
                    } else if (this.state === "rejected") {
                        if (onRejected) {
                            const result = onRejected(this.reason);
                            resolve(result instanceof OptimizedPromise ? result : result);
                        } else {
                            reject(this.reason);
                        }
                    }
                } catch (error) {
                    reject(error);
                }
            };

            if (this.state === "pending") {
                this.callbacks.push({
                    onFulfilled: handleCallback,
                    onRejected: handleCallback,
                });
            } else {
                queueMicrotask(handleCallback);
            }
        });
    }

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

    finally(callback) {
        return this.then(
            (value) => {
                callback();
                return value;
            },
            (reason) => {
                callback();
                throw reason;
            }
        );
    }

    static resolve(value) {
        return new OptimizedPromise((resolve) => resolve(value));
    }

    static reject(reason) {
        return new OptimizedPromise((_, reject) => reject(reason));
    }

    static all(promises) {
        return new OptimizedPromise((resolve, reject) => {
            const results = [];
            let completed = 0;

            promises.forEach((promise, index) => {
                OptimizedPromise.resolve(promise)
                    .then((value) => {
                        results[index] = value;
                        completed++;
                        if (completed === promises.length) resolve(results);
                    })
                    .catch(reject);
            });
        });
    }

    static race(promises) {
        return new OptimizedPromise((resolve, reject) => {
            promises.forEach((promise) => {
                OptimizedPromise.resolve(promise).then(resolve).catch(reject);
            });
        });
    }
}

实现细节与优化点

1. 状态管理

通过 state 来记录当前的状态,并在 resolvereject 中处理状态流转。不可逆状态设计确保了操作的幂等性。

2. 异步调度

使用 queueMicrotask 实现微任务调度,确保回调函数异步执行,比传统的 setTimeout 更高效且符合 ECMAScript 规范。

3. 链式调用

then 方法中处理返回值类型,支持普通值和嵌套 Promise

  • 如果返回普通值,直接 resolve
  • 如果返回另一个 Promise,则等待其完成再继续。

4. 静态方法

  • resolvereject :快速创建已完成或已拒绝的 Promise
  • all :并发处理多个 Promise,返回所有完成结果。
  • race :返回第一个完成的 Promise,无论成功还是失败。

完整测试用例

测试基础功能

javascript 复制代码
const p1 = OptimizedPromise.resolve(1);
p1.then((value) => console.log("Resolved:", value)); // 输出 Resolved: 1

const p2 = new OptimizedPromise((resolve, reject) => {
    setTimeout(() => resolve(2), 100);
});
p2.then((value) => console.log("Async Resolved:", value)); // 输出 Async Resolved: 2

测试链式调用

javascript 复制代码
p1.then((value) => value * 2)
  .then((value) => new OptimizedPromise((resolve) => resolve(value + 3)))
  .then((value) => console.log("Chained Value:", value)); // 输出 Chained Value: 5

测试 allrace

javascript 复制代码
const p3 = OptimizedPromise.reject("Error!");
OptimizedPromise.all([p1, p2]).then((values) => console.log("All Resolved:", values)); // 输出 All Resolved: [1, 2]
OptimizedPromise.race([p2, p3]).catch((reason) => console.log("Race Rejected:", reason)); // 输出 Race Rejected: Error!

测试 finally

javascript 复制代码
p2.finally(() => console.log("Cleaning up...")).then((value) => console.log("Value:", value));
// 输出:
// Cleaning up...
// Value: 2

总结

这份优化后的 Promise 实现从状态管理、异步调度、链式调用到静态方法都做到了高效与规范。通过支持 finally 和链式嵌套,解决了真实开发中可能遇到的复杂场景。

核心亮点

  1. 微任务调度确保异步回调顺序。
  2. 支持所有常见静态方法(resolverejectallrace)。
  3. 完整覆盖异常处理和链式调用。

通过深入理解其实现细节,你不仅可以更好地掌握 Promise 的原理,还能在实际开发中写出更高效、健壮的异步代码。

相关推荐
百思可瑞教育10 分钟前
Vue.config.js中的Webpack配置、优化及多页面应用开发
前端·javascript·vue.js·webpack·uni-app·北京百思教育
患得患失94912 分钟前
【前端】【高德地图WebJs】【知识体系搭建】面要素知识点——>多边形,圆形, 矩形,图形编辑器
前端·编辑器·高德地图·amap
歪歪10015 分钟前
webpack 配置文件中 mode 有哪些模式?
开发语言·前端·javascript·webpack·前端框架·node.js
歪歪10019 分钟前
如何配置Webpack以实现按需加载模块?
开发语言·前端·webpack·node.js
面向星辰3 小时前
html各种常用标签
前端·javascript·html
梦6503 小时前
HTML新属性
前端
东风西巷5 小时前
PDFgear:免费全能的PDF处理工具
前端·pdf·软件需求
森之鸟6 小时前
Mac电脑上如何打印出字体图标
前端·javascript·macos
mCell6 小时前
GSAP 入门指南
前端·javascript·动效
gnip7 小时前
组件循环引用依赖问题处理
前端·javascript