手写简化版 Promise 详解

在JavaScript中,Promise 是一种用于处理异步操作的强大机制。虽然现代JavaScript环境(如Node.js和浏览器)已经内置了功能完备的 Promise 实现,但了解如何手写一个简化版的 Promise 可以帮助我们深入理解其内部工作原理。

一、Promise 的基本结构

一个基本的 Promise 实现需要包含几个关键部分:状态管理、执行器(executor)函数、以及处理成功和失败的回调函数的队列。

1. 状态管理

Promise 有三种状态:pending(等待中)、fulfilled(已成功)、rejected(已失败)。状态一旦改变就不能再变。

class MyPromise {
    constructor(executor) {
        this.status = 'pending';
        this.value = undefined;
        this.reason = undefined;
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];

        try {
            executor(this._resolve.bind(this), this._reject.bind(this));
        } catch (error) {
            this._reject(error);
        }
    }

    _resolve(value) {
        if (this.status === 'pending') {
            this.status = 'fulfilled';
            this.value = value;
            this.onFulfilledCallbacks.forEach(fn => fn(value));
        }
    }

    _reject(reason) {
        if (this.status === 'pending') {
            this.status = 'rejected';
            this.reason = reason;
            this.onRejectedCallbacks.forEach(fn => fn(reason));
        }
    }
}
2. 执行器函数

执行器函数是传递给 Promise 构造函数的参数,它接受两个函数作为参数:resolvereject。这两个函数用于改变 Promise 的状态。

二、then 方法

then 方法是 Promise 的核心,它允许我们为 Promise 成功或失败时注册回调函数。

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };

    if (this.status === 'fulfilled') {
        setTimeout(() => onFulfilled(this.value));
    } else if (this.status === 'rejected') {
        setTimeout(() => onRejected(this.reason));
    } else {
        this.onFulfilledCallbacks.push(onFulfilled);
        this.onRejectedCallbacks.push(onRejected);
    }

    // 链式调用和错误传递的实现(简化版)
    return new MyPromise((resolve, reject) => {
        if (this.status === 'fulfilled') {
            setTimeout(() => {
                try {
                    const result = onFulfilled(this.value);
                    // 这里简化处理,假设result不是Promise
                    resolve(result);
                } catch (error) {
                    reject(error);
                }
            });
        } else if (this.status === 'rejected') {
            // 类似地处理rejected状态
        }
    });
};

注意:上面的 then 方法实现是简化的,它只处理了 fulfilled 状态下的情况,并且没有处理 onFulfilledonRejected 返回 Promise 的情况。在完整的实现中,你需要添加对这些情况的处理。

三、静态方法和链式调用

为了支持 Promise 链式调用和错误传递,then 方法需要返回一个新的 Promise。此外,Promise 还有一些静态方法,如 Promise.allPromise.race,这些方法的实现需要额外的逻辑来跟踪和管理多个 Promise 的状态。

四、优化与完整实现

在完整的 Promise 实现中,你需要考虑更多的边界情况和优化。例如:

  • 处理 then 方法中 onFulfilledonRejected 抛出异常的情况。
  • 支持 then 方法返回 Promise 的情况,这通常通过递归调用 then 方法来实现。
  • 实现 Promise.allPromise.race 等静态方法。
  • 优化性能,例如通过减少不必要的回调函数调用次数。

手写一个完整的 Promise 实现是一个复杂但非常有教育意义的过程,因为它涉及到异步编程、状态管理、以及错误处理等多个方面。下面是一个更加完整但简化的 Promise 实现示例,这个实现将包括基本的状态管理、then 方法(支持返回 Promise 的情况)、以及链式调用和错误传递的支持。

class MyPromise {
    constructor(executor) {
        this.status = 'pending';
        this.value = undefined;
        this.reason = undefined;
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];

        const resolve = (value) => {
            if (this.status === 'pending') {
                this.status = 'fulfilled';
                this.value = value;
                this.onFulfilledCallbacks.forEach(fn => {
                    this._handleNextPromise(fn, value);
                });
            }
        };

        const reject = (reason) => {
            if (this.status === 'pending') {
                this.status = 'rejected';
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => {
                    this._handleNextPromise(fn, reason);
                });
            }
        };

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

    _handleNextPromise(callback, valueOrReason) {
        let returnValue;
        try {
            returnValue = callback(valueOrReason);
        } catch (error) {
            return this._rejectPromise(error);
        }

        if (returnValue instanceof MyPromise) {
            returnValue.then(
                this._resolvePromise.bind(this),
                this._rejectPromise.bind(this)
            );
        } else {
            this._resolvePromise(returnValue);
        }
    }

    _resolvePromise(value) {
        // 在这里简化处理,不考虑循环引用等情况
        this._resolve(value);
    }

    _rejectPromise(reason) {
        this._reject(reason);
    }

    _resolve(value) {
        // 简化处理,不再重复
    }

    _reject(reason) {
        // 简化处理,不再重复
    }

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };

        return new MyPromise((resolve, reject) => {
            if (this.status === 'fulfilled') {
                setTimeout(() => {
                    try {
                        this._handleNextPromise(onFulfilled, this.value);
                    } catch (error) {
                        reject(error);
                    }
                });
            } else if (this.status === 'rejected') {
                setTimeout(() => {
                    try {
                        this._handleNextPromise(onRejected, this.reason);
                    } catch (error) {
                        reject(error);
                    }
                });
            } else {
                this.onFulfilledCallbacks.push(onFulfilled);
                this.onRejectedCallbacks.push(onRejected);
            }
        });
    }

    // 这里可以添加更多方法,如 catch, finally, static 方法等
}


new MyPromise((resolve, reject) => {
    setTimeout(() => resolve(42), 1000);
}).then(value => {
    console.log(value); // 输出 42
    return new MyPromise((resolve, reject) => {
        setTimeout(() => resolve(value * 2), 500);
    });
}).then(value => {
    console.log(value); // 输出 84
});

注意

  1. 这个实现仍然有许多简化和未处理的情况,比如循环引用的检测、Promise 规范化(即处理 then 方法返回的不是 Promise 的情况)等。
  2. _handleNextPromise 方法中,我们尝试执行回调函数,并根据返回值进行进一步的处理。如果回调函数返回了一个 MyPromise 实例,我们将该实例的 then 方法与内部的 resolvereject 方法绑定,以实现链式调用。
  3. setTimeout 的使用是为了模拟异步操作,它确保 then 方法中的回调函数在下一个事件循环中执行,从而模拟真实的异步行为。

在实际应用中,Promise 的实现会更加复杂,特别是要处理各种边界情况和优化性能。不过,基于我们之前的简化实现,我们可以继续添加一些重要的功能,比如 catch 方法和 finally 方法,以及处理 Promise 静态方法如 Promise.allPromise.racePromise.resolvePromise.reject

下面是一个扩展了 catchfinally 方法的 MyPromise 实现:

class MyPromise {
    // ... (之前的代码保持不变)

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

    finally(onFinally) {
        return this.then(
            value => MyPromise.resolve(onFinally()).then(() => value),
            reason => MyPromise.reject(onFinally()).then(() => { throw reason; })
        );
    }

    // 静态方法
    static resolve(value) {
        return new MyPromise(resolve => resolve(value));
    }

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

    // 注意:这里不实现 Promise.all, Promise.race 等,因为它们需要额外的逻辑来处理多个 Promise
}

// 使用示例
MyPromise.resolve(1)
    .then(x => x + 1)
    .then(x => {
        if (x < 5) {
            throw new Error('x is not greater than or equal to 5');
        }
        return x;
    })
    .catch(err => console.error('Caught an error:', err))
    .finally(() => console.log('Finally block executed'));

// 输出:
// Caught an error: Error: x is not greater than or equal to 5
// Finally block executed

在这个扩展中,catch 方法简单地调用了 then 方法,并传递了 null 作为第一个参数(即不处理 fulfilled 状态),只传递了错误处理函数作为第二个参数。

finally 方法稍微复杂一些,它接受一个回调函数 onFinally,该函数会在 Promise 结束时(无论是 fulfilled 还是 rejected)执行。finally 方法需要确保原始 Promise 的结果(值或错误)被正确传递。这里,我们使用了 MyPromise.resolve(onFinally()).then(...) 来确保 onFinally 函数被异步执行,并且其结果不会影响原始 Promise 的值或错误。如果 onFinally 函数返回了一个 Promise,我们需要等待它解决后再继续传递原始 Promise 的结果。

请注意,这里的 MyPromise.resolve(onFinally()).then(...) 假设 onFinally 函数不会抛出错误,或者即使它抛出错误,我们也希望忽略它并继续执行原始 Promise 的后续处理。在实际应用中,你可能需要添加额外的错误处理逻辑来确保 finally 块中的错误被适当地捕获和处理。

最后,MyPromise.resolveMyPromise.reject 是静态方法,用于快速创建已解决或已拒绝的 Promise 对象。这些方法在实际应用中非常有用,因为它们允许你以编程方式创建 Promise 对象,而无需定义 executor 函数。

当然,我们可以继续扩展MyPromise类,以下是一个简单的Promise.allPromise.race的模拟实现,添加到我们的MyPromise类中:

class MyPromise {
    // ... (之前的代码保持不变,包括then, catch, finally, resolve, reject)

    // 静态方法 Promise.all
    static all(promises) {
        return new MyPromise((resolve, reject) => {
            let results = [];
            let remaining = promises.length;

            if (remaining === 0) {
                resolve(results);
                return;
            }

            promises.forEach((p, index) => {
                MyPromise.resolve(p).then(
                    value => {
                        results[index] = value;
                        if (--remaining === 0) {
                            resolve(results);
                        }
                    },
                    err => {
                        reject(err);
                    }
                );
            });
        });
    }

    // 静态方法 Promise.race
    static race(promises) {
        return new MyPromise((resolve, reject) => {
            promises.forEach(p => {
                MyPromise.resolve(p).then(resolve, reject);
            });
        });
    }

    // ... (其他代码)
}

// 使用示例
MyPromise.all([
    MyPromise.resolve(1),
    MyPromise.resolve(2).then(x => x * 2),
    MyPromise.reject(new Error('Failed')),
    MyPromise.resolve(4)
]).then(values => console.log(values))
.catch(err => console.error('Failed:', err));

// 输出: Failed: Error: Failed

MyPromise.race([
    MyPromise.resolve(1).delay(5000), // 注意:这里假设delay是一个自定义扩展,实际Promise没有
    MyPromise.resolve(2)
]).then(value => console.log(value));

// 输出: 2

// 注意:由于JavaScript原生的Promise没有delay方法,这里只是一个示例。
// 在实际使用中,你可能需要使用setTimeout或其他方式来实现延迟。

注意:上面的delay方法并不是Promise原生的一部分,我只是用它来模拟一个长时间运行的Promise

MyPromise.all方法等待所有给定的Promise都成功解决,并将它们的值作为一个数组返回。如果任何一个Promise被拒绝,那么MyPromise.all立即以相同的错误拒绝。

MyPromise.race方法返回一个新的Promise,该Promise在所有给定的Promise中解决得最快的一个解决时解决,其解决值与那个最快的Promise的解决值相同。如果任何一个Promise被拒绝,返回的Promise也立即以相同的错误被拒绝。

这些扩展使得MyPromise类更加强大和灵活,能够处理更复杂的异步逻辑。然而,请注意,为了完全模拟原生Promise的行为,还需要考虑更多的边界情况和优化。

结论:

通过实现MyPromise类及其扩展功能,我们不仅加深了对Promise工作原理的理解,还学会了如何以编程方式创建和管理异步操作。虽然MyPromise类在功能上可能不如原生Promise完整,但它为学习异步编程和Promise模式提供了一个很好的起点。随着对JavaScript和异步编程的进一步学习,你将能够更深入地理解和应用这些概念。

本文由mdnice多平台发布


jywud
42 声望7 粉丝