手写符合A+规范的Promise

本专栏聚焦Promise的核心原理与高级应用,包含: ✓ Promise A+规范深度解读 ✓ 手写实现与源码分析 ✓ 异步编程设计模式 ✓ 性能调优与错误处理
适合有JavaScript基础,希望深入异步编程的开发者。我们将用最少的篇幅,讲透最核心的知识。

引言

在前面的文章中,我们详细讲解了Promise的核心原理及相关的方法,本文将从零开始,手写一个功能完整的Promise类,深入理解Promise的工作原理。

Promise核心概念回顾

在开始实现之前,我们先回顾一下Promise的核心特性:

  • 三种状态:pending(等待)、fulfilled(已成功)、rejected(已失败)。
  • 状态不可逆:一旦状态改变,就不能再变。
  • 链式调用:then方法返回一个新的Promise。
  • 异步执行:回调函数总是异步执行。
  • 错误冒泡:错误会一直向后传递,直到被捕获。

基础骨架实现

构造函数

代码实现

javascript 复制代码
class Promise {
		// 构造函数接收一个执行器函数executor
    constructor(executor) {

        this.promiseState = 'pending';  // 声明初始状态
        this.promiseResult = null; // Priomise结果
        // 存储成功和失败的回调函数
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];

        // 声明resolve和reject两个函数
        const resolve = (value) => {
            // 判断状态是否为pending
            if (this.promiseState !== 'pending') return;
            // 1、修改对象状态
            this.promiseState = 'fulfilled';
            // 2、保存成功值
            this.promiseResult = value;
            // 3、调用成功回调函数
            if (this.onResolvedCallbacks.length > 0) {
                this.onResolvedCallbacks.forEach(item => item(value));
            }
        }
        const reject = (reason) => {
            // 判断状态是否为pending
            if (this.promiseState !== 'pending') return;
            // 1、修改对象状态
            this.promiseState = 'rejected';
            // 2、保存失败原因
            this.promiseResult = reason;
            // 3、调用失败回调函数
            if (this.onRejectedCallbacks.length > 0) {
                this.onRejectedCallbacks.forEach(item => item(reason));
            }
        }
        try {
            // 执行器函数executor是同步调用
            executor(resolve, reject);
        }
        catch (e) {
            // 执行器抛出的异常直接reject
            reject(e);
        }
    }
}

关键点解析

  • 状态机:使用promiseState记录当前状态,当状态不为'pending'时,说明状态已经改变过了,直接返回,不进行任何处理;否则,才进行修改状态、保存结果、执行调用等操作。
  • 结果存储:promiseResult存储成功值或失败原因。
  • 回调队列:维护成功和失败回调队列,支持多次then调用。
  • 异步执行:使用setTimeout确保回调异步执行。

链式调用核心实现

then()方法的基本结构

代码实现

javascript 复制代码
then(onResolved, onRejected) {
    // 如果不是函数则创建一个默认函数
    onResolved = typeof onResolved === 'function' ? onResolved : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

    // 返回新的Promise
    const promise2 = new Promise((resolve, reject) => {
        // 处理resolved状态的函数
        const handleResolved = () => {
            // 使用setTimeout确保异步执行
            setTimeout(() => {
                try {
                    const x = onResolved(this.promiseResult);
                    // 处理返回值,可能是普通值或Promise
                    this.resolvePromise(promise2, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            }, 0);
        };

        // 处理rejected状态的函数
        const handleRejected = () => {
            setTimeout(() => {
                try {
                    const x = onRejected(this.promiseResult);
                    this.resolvePromise(promise2, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            }, 0);
        };

        // 根据当前状态处理
        if (this.promiseState === 'fulfilled') {
            handleResolved();
        } else if (this.promiseState === 'rejected') {
            handleRejected();
        } else if (this.promiseState === 'pending') {
            // 如果是pending状态,将回调函数存储起来
            this.onResolvedCallbacks.push(() => handleResolved());
            this.onRejectedCallbacks.push(() => handleRejected());
        }
    });

    return promise2;
}

关键点解析

  • 值透传:如果 onResolved 不是函数,创建一个返回原值的函数 value => value
  • 错误冒泡:如果 onRejected 不是函数,创建一个抛出错误的函数,让错误继续向下传递。
  • 链式调用的基础:每个 then 必须返回新的 Promise,且返回的 Promise 是独立的,状态不受前一个 Promise 影响。
  • 异步执行包装器(handleResolved/handleRejected):模拟微任务行为,确保回调函数总是异步执行。

Promise解析过程(核心难点)

代码实现

javascript 复制代码
resolvePromise(promise2, x, resolve, reject) {
    // 防止循环引用
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise'));
    }

    // 标记是否已被调用,防止多次调用
    let called = false;

    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            // 获取then方法
            const then = x.then;

            if (typeof then === 'function') {
                // 如果x有then方法,则将其视为Promise
                then.call(
                    x,
                    // resolvePromise
                    y => {
                        if (called) return;
                        called = true;
                        // 递归解析
                        this.resolvePromise(promise2, y, resolve, reject);
                    },
                    // rejectPromise
                    r => {
                        if (called) return;
                        called = true;
                        reject(r);
                    }
                );
            } else {
                // 如果x是普通对象或函数,直接resolve
                resolve(x);
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(error);
        }
    } else {
        // 如果x是基本类型,直接resolve
        resolve(x);
    }
}

为什么需要resolvePromise

  • 防止循环引用导致的无限递归。
  • 确保 Promise 解析过程符合规范。

这部分,我认为是 Promise 实现中最复杂的部分!

实例方法实现

catch()方法

代码实现

javascript 复制代码
catch(onRejected) {
    return this.then(null, onRejected);
}

finally()方法

代码实现

javascript 复制代码
finally(onFinally) {
    // 需要先检查 onFinally 是否为函数
    if (typeof onFinally !== 'function') {
        return this.then();
    }
    return this.then(
        value => Promise.resolve(onFinally()).then(() => value),
        reason => Promise.resolve(onFinally()).then(() => { throw reason })
    );
}

静态方法实现

Promise.resolve() / Promise.reject()

代码实现

javascript 复制代码
static resolve(value) {
    // 如果已经是Promise实例,直接返回
    if (value instanceof Promise) {
        return value;
    }

    // 普通值
    return new Promise(resolve => {
        resolve(value);
    });
}

// Promise静态方法reject
static reject(reason) {
    return new Promise((_, reject) => {
        reject(reason);
    });
}

从上述代码中,我们可以看出:resolve() 方法比 reject() 方法多了一层判断逻辑,即:如果已经是Promise实例,直接返回 ,这部分代码可以保障 Promise.resolve() 方法的幂等性。

Promise.all():所有成功,才算成功

代码实现

javascript 复制代码
static all(promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises) || promises.length === 0) {
            return reject(new TypeError('参数必须是数组'));
        }

        const results = [];
        let completedCount = 0;

        promises.forEach((promise, index) => {
            Promise.resolve(promise).then(
                value => {
                    results[index] = value;
                    completedCount++;

                    if (completedCount === promises.length) {
                        resolve(results);
                    }
                },
                reject  // 直接传递reject函数
            );
        });
    });
}

Promise.race():竞速,谁最先完成

代码实现

javascript 复制代码
static race(promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises) || promises.length === 0) {
            return reject(new TypeError('参数必须是数组'));
        }

        promises.forEach(promise => {
            Promise.resolve(promise).then(resolve, reject);
        });
    });
}

Promise.allSettled():全部完成,记录所有结果

代码实现

javascript 复制代码
static allSettled(promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises) || promises.length === 0) {
            return reject(new TypeError('参数必须是数组'));
        }

        const results = [];
        let completedCount = 0;

        const processResult = (index, value, status) => {
            results[index] = status === 'fulfilled'
                ? { status: 'fulfilled', value }
                : { status: 'rejected', reason: value };

            completedCount++;

            if (completedCount === promises.length) {
                resolve(results);
            }
        };

        promises.forEach((promise, index) => {
            Promise.resolve(promise).then(
                value => processResult(index, value, 'fulfilled'),
                reason => processResult(index, reason, 'rejected')
            );
        });
    });
}

Promise.any():任意一个成功,都算成功

代码实现

javascript 复制代码
static any(promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises) || promises.length === 0) {
            return reject(new TypeError('参数必须是数组'));
        }

        const errors = [];
        let rejectedCount = 0;

        promises.forEach((promise, index) => {
            Promise.resolve(promise).then(
                value => {
                    resolve(value);
                },
                reason => {
                    errors[index] = reason;
                    rejectedCount++;
                    if (rejectedCount === promises.length) {
                        reject(new AggregateError(errors, '所有Promise都被拒绝'));
                    }
                }
            );
        });
    });
}

完整代码

javascript 复制代码
class Promise {
    // 构造函数接收一个执行器函数executor
    constructor(executor) {

        this.promiseState = 'pending';  // 声明初始状态
        this.promiseResult = null; // Priomise结果
        // 存储成功和失败的回调函数
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];

        // 声明resolve和reject两个函数
        const resolve = (value) => {
            // 判断状态是否为pending
            if (this.promiseState !== 'pending') return;
            // 1、修改对象状态
            this.promiseState = 'fulfilled';
            // 2、保存成功值
            this.promiseResult = value;
            // 3、调用成功回调函数
            if (this.onResolvedCallbacks.length > 0) {
                this.onResolvedCallbacks.forEach(item => item(value));
            }
        }
        const reject = (reason) => {
            // 判断状态是否为pending
            if (this.promiseState !== 'pending') return;
            // 1、修改对象状态
            this.promiseState = 'rejected';
            // 2、保存失败原因
            this.promiseResult = reason;
            // 3、调用失败回调函数
            if (this.onRejectedCallbacks.length > 0) {
                this.onRejectedCallbacks.forEach(item => item(reason));
            }
        }
        try {
            // 执行器函数executor是同步调用
            executor(resolve, reject);
        }
        catch (e) {
            // 执行器抛出的异常直接reject
            reject(e);
        }
    }

    // then方法接收两个参数onResolved和onRejected
    then(onResolved, onRejected) {
        // 如果不是函数则创建一个默认函数
        onResolved = typeof onResolved === 'function' ? onResolved : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

        // 返回新的Promise
        const promise2 = new Promise((resolve, reject) => {
            // 处理resolved状态的函数
            const handleResolved = () => {
                // 使用setTimeout确保异步执行
                setTimeout(() => {
                    try {
                        const x = onResolved(this.promiseResult);
                        // 处理返回值,可能是普通值或Promise
                        this.resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            };

            // 处理rejected状态的函数
            const handleRejected = () => {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.promiseResult);
                        this.resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            };

            // 根据当前状态处理
            if (this.promiseState === 'fulfilled') {
                handleResolved();
            } else if (this.promiseState === 'rejected') {
                handleRejected();
            } else if (this.promiseState === 'pending') {
                // 如果是pending状态,将回调函数存储起来
                this.onResolvedCallbacks.push(() => handleResolved());
                this.onRejectedCallbacks.push(() => handleRejected());
            }
        });

        return promise2;
    }

    // 处理Promise解析过程
    resolvePromise(promise2, x, resolve, reject) {
        // 防止循环引用
        if (promise2 === x) {
            return reject(new TypeError('Chaining cycle detected for promise'));
        }

        // 标记是否已被调用,防止多次调用
        let called = false;

        if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
            try {
                // 获取then方法
                const then = x.then;

                if (typeof then === 'function') {
                    // 如果x有then方法,则将其视为Promise
                    then.call(
                        x,
                        // resolvePromise
                        y => {
                            if (called) return;
                            called = true;
                            // 递归解析
                            this.resolvePromise(promise2, y, resolve, reject);
                        },
                        // rejectPromise
                        r => {
                            if (called) return;
                            called = true;
                            reject(r);
                        }
                    );
                } else {
                    // 如果x是普通对象或函数,直接resolve
                    resolve(x);
                }
            } catch (error) {
                if (called) return;
                called = true;
                reject(error);
            }
        } else {
            // 如果x是基本类型,直接resolve
            resolve(x);
        }
    }

    // catch方法接收一个参数onRejected
    catch(onRejected) {
        return this.then(null, onRejected);
    }

    // finally方法接收一个参数onFinally
    finally(onFinally) {
        // 需要先检查 onFinally 是否为函数
        if (typeof onFinally !== 'function') {
            return this.then();
        }
        return this.then(
            value => Promise.resolve(onFinally()).then(() => value),
            reason => Promise.resolve(onFinally()).then(() => { throw reason })
        );
    }

    // Promise静态方法resolve
    static resolve(value) {
        // 如果已经是Promise实例,直接返回
        if (value instanceof Promise) {
            return value;
        }

        // 普通值
        return new Promise(resolve => {
            resolve(value);
        });
    }

    // Promise静态方法reject
    static reject(reason) {
        return new Promise((_, reject) => {
            reject(reason);
        });
    }

    // Promise静态方法all
    static all(promises) {
        return new Promise((resolve, reject) => {
            if (!Array.isArray(promises) || promises.length === 0) {
                return reject(new TypeError('参数必须是数组'));
            }

            const results = [];
            let completedCount = 0;

            promises.forEach((promise, index) => {
                Promise.resolve(promise).then(
                    value => {
                        results[index] = value;
                        completedCount++;

                        if (completedCount === promises.length) {
                            resolve(results);
                        }
                    },
                    reject  // 直接传递reject函数
                );
            });
        });
    }

    // Promise静态方法race
    static race(promises) {
        return new Promise((resolve, reject) => {
            if (!Array.isArray(promises) || promises.length === 0) {
                return reject(new TypeError('参数必须是数组'));
            }

            promises.forEach(promise => {
                Promise.resolve(promise).then(resolve, reject);
            });
        });
    }

    // Promise静态方法allSettled
    static allSettled(promises) {
        return new Promise((resolve, reject) => {
            if (!Array.isArray(promises) || promises.length === 0) {
                return reject(new TypeError('参数必须是数组'));
            }

            const results = [];
            let completedCount = 0;

            const processResult = (index, value, status) => {
                results[index] = status === 'fulfilled'
                    ? { status: 'fulfilled', value }
                    : { status: 'rejected', reason: value };

                completedCount++;

                if (completedCount === promises.length) {
                    resolve(results);
                }
            };

            promises.forEach((promise, index) => {
                Promise.resolve(promise).then(
                    value => processResult(index, value, 'fulfilled'),
                    reason => processResult(index, reason, 'rejected')
                );
            });
        });
    }


    // Promise静态方法any
    static any(promises) {
        return new Promise((resolve, reject) => {
            if (!Array.isArray(promises) || promises.length === 0) {
                return reject(new TypeError('参数必须是数组'));
            }

            const errors = [];
            let rejectedCount = 0;

            promises.forEach((promise, index) => {
                Promise.resolve(promise).then(
                    value => {
                        resolve(value);
                    },
                    reason => {
                        errors[index] = reason;
                        rejectedCount++;
                        if (rejectedCount === promises.length) {
                            reject(new AggregateError(errors, '所有Promise都被拒绝'));
                        }
                    }
                );
            });
        });
    }
}

结语

本文通过手写实现Promise,深入理解了Promise的工作原理,掌握了异步编程的核心思想。虽然实际的Promise实现更为复杂(考虑更多边界情况和性能优化),但本文的实现已经涵盖了Promise的核心功能。对于文章中错误的地方或者有任何问题,欢迎在评论区留言讨论!

相关推荐
张元清2 小时前
大白话讲 React2Shell 漏洞:智能家居的语音助手危机
前端·javascript·react.js
小明记账簿_微信小程序2 小时前
一篇文章教会你接入Deepseek API
前端
若凡SEO2 小时前
深圳优势产业(电子 / 机械)出海独立站运营白皮书
大数据·前端·搜索引擎
踢球的打工仔2 小时前
typescript-void和never
前端·javascript·typescript
hugo_im2 小时前
GrapesJS 完全指南:从零构建你的可视化拖拽编辑器
前端·javascript·前端框架
用户904706683572 小时前
nuxt 路由一篇讲清楚
前端
盘子素2 小时前
前端实现跳转子系统,但限制只能跳转一次
前端·javascript
Anita_Sun2 小时前
Lodash 源码解读与原理分析 - Lodash 前世今生:设计原则与演进脉络
前端
爱吃羊的老虎2 小时前
Streamlit:快速创建应用界面,无需了解 Web 开发
前端·python