深入理解 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 的原理,还能在实际开发中写出更高效、健壮的异步代码。

相关推荐
誰氵难浔30 分钟前
uniapp获取元素高度不准确问题解决
前端
Y_coder1 小时前
【CSS】渐变光晕
前端·javascript·css
半点寒12W1 小时前
CSS3 3D 转换介绍
前端·3d·css3
熬夜患者2 小时前
HTML学习笔记(4)
前端·css·html
2401_882727972 小时前
web组态可视化编辑器
前端·后端·物联网·低代码·编辑器
谢道韫6662 小时前
今日总结 2025-01-14
前端
大邳草民3 小时前
Vue 项目中引入外部脚本的方式
前端·javascript·vue.js·笔记
iDestin3 小时前
解决 chls.pro/ssl 无法进入问题
前端·代理·charles
半点寒12W4 小时前
CSS3 动画详解
前端·css·css3
桂月二二4 小时前
深入探索 Vue.js 组件开发中的最新技术:Teleport 和 Suspense 的使用
前端·javascript·vue.js