• 作者:老汪软件技巧
  • 发表时间:2024-10-16 17:02
  • 浏览量:

各位小伙伴们,今天我们来实现一个面试中经常会被问到的内容——手写实现一个简版的 Promise。通过这个过程,不仅能够加深大家对 Promise 的理解,还能掌握其底层机制。

在这篇文章中,我们将一步步手写一个自定义的 MyPromise,通过代码讲解,帮助大家深入理解 Promise 的执行逻辑。我们将从基础的 Promise 定义、状态管理、then 方法的实现、回调函数收集等方面入手,逐步构建一个符合 Promise/A+ 规范的 Promise。

什么是 Promise?

首先,简单介绍一下 Promise。它是 JavaScript 用来处理异步操作的一种方式,它可以将异步代码转换为类似同步的形式。Promise 有三种状态:

pending(等待中):初始状态,既没有被解决也没有被拒绝。fulfilled(已完成):操作成功完成。rejected(已拒绝):操作失败。

一个 Promise 的状态一旦从 pending 变为 fulfilled 或 rejected,就不能再次更改。

接下来,我们开始手写一个 Promise 类,命名为 MyPromise。

构建基础的 MyPromise 类

我们先从基本的构造函数入手。Promise 的构造函数接收一个执行器(executor),executor 是一个包含两个参数 resolve 和 reject 的函数。我们需要在构造函数中定义 state 来表示 Promise 的当前状态,并且初始状态应该是 pending。

class MyPromise {
    constructor(executor) {
        // 定义初始状态为 pending
        this.state = 'pending';
        // 保存 resolve 和 reject 的值
        this.value = undefined;
        this.reason = undefined;
        
        // 用于存储 .then 和 .catch 中的回调函数
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];
        // 定义 resolve 函数
        const resolve = (val) => {
            if (this.state === 'pending') {
                // 将状态改为 fulfilled
                this.state = 'fulfilled';
                // 保存 resolve 的值
                this.value = val;
                // 依次执行 .then 中的回调
                this.onFulfilledCallbacks.forEach(fn => fn());
            }
        };
        // 定义 reject 函数
        const reject = (val) => {
            if (this.state === 'pending') {
                // 将状态改为 rejected
                this.state = 'rejected';
                // 保存 reject 的值
                this.reason = val;
                // 依次执行 .catch 中的回调
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };
        // 调用 executor,并传入 resolve 和 reject
        executor(resolve, reject);
    }
}

我们在构造函数里定义了 this.state,初始值为 pending。resolve 函数的作用是,当调用时将 state 从 pending 变为 fulfilled,并保存传入的 val。

onFulfilledCallbacks 和 onRejectedCallbacks 是用来存储 then 和 catch 中的回调函数,当 resolve 或 reject 被调用时依次执行。

实现 then 方法

接下来,我们实现 then 方法,它接收两个回调函数——一个是成功时执行的 onFulfilled,另一个是失败时执行的 onRejected。

then(onFulfilled, onRejected) {
    // 检查 onFulfilled 和 onRejected 是否为函数
    if (onFulfilled && typeof onFulfilled !== 'function' || onRejected && typeof onRejected !== 'function') {
        throw new Error('then 必须接受一个函数');
    }
    // 返回一个新的 MyPromise 实例,实现链式调用
    return new MyPromise((resolve, reject) => {
        // 如果当前状态是 fulfilled,则执行 onFulfilled 回调
        if (this.state === 'fulfilled') {
            setTimeout(() => {
                try {
                    const result = onFulfilled(this.value);  // 传入 resolve 的值
                    resolve(result);  // 返回的值传递给下一个 then
                } catch (error) {
                    reject(error);  // 如果出错,捕获并传递给下一个 catch
                }
            });
        }
        // 如果当前状态是 rejected,则执行 onRejected 回调
        if (this.state === 'rejected') {
            setTimeout(() => {
                try {
                    const result = onRejected(this.reason);  // 传入 reject 的原因
                    resolve(result);
                } catch (error) {
                    reject(error);
                }
            });
        }
        // 如果当前状态是 pending,则将回调存起来,等到状态改变后再执行
        if (this.state === 'pending') {
            this.onFulfilledCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        const result = onFulfilled(this.value);
                        resolve(result);
                    } catch (error) {
                        reject(error);
                    }
                });
            });
            this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        const result = onRejected(this.reason);
                        resolve(result);
                    } catch (error) {
                        reject(error);
                    }
                });
            });
        }
    });
}

了解代码__对代码进行解释的软件

实现 catch 方法

catch 方法是 then 方法的一个变种,它只关注失败的情况。我们可以直接使用 then 方法来实现 catch。

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

catch 方法只处理 rejected 状态,因此它只传入 onRejected 回调,将 onFulfilled 设置为 null。

Promise.all

Promise.all 是一个静态方法,接收一个可迭代对象(如数组),这个对象的每个元素都是 Promise。它的主要作用是:

并行执行多个 Promise。当所有 Promise 都完成(即全部解决)时,返回一个新的 Promise,这个新的 Promise 的结果是一个包含每个 Promise 结果的数组,顺序与传入的数组顺序一致。如果其中任何一个 Promise 被拒绝,则 Promise.all 立即返回被拒绝的 Promise,并且不会等待其他 Promise 的完成。

使用示例:

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3])
  .then((values) => {
    console.log(values); // 输出: [3, 42, "foo"]
  });

Promise.all 与其他 Promise 方法的区别

在 Promise API 中,除了 Promise.all 之外,还有几个常用的静态方法,例如 Promise.race、Promise.any 和 Promise.allSettled。它们的主要区别如下:

Promise.all:等到所有 Promise 都成功才返回结果,如果其中任何一个 Promise 被拒绝,则整个操作失败。Promise.race:只要数组中任何一个 Promise 首先完成(无论是解决还是拒绝),Promise.race 就会返回该 Promise 的结果。Promise.any:只要数组中任何一个 Promise 首先解决,Promise.any 就会返回该解决值。只有当所有 Promise 都被拒绝时,它才会返回拒绝。Promise.allSettled:等待所有 Promise 都解决或拒绝,返回的结果数组包含每个 Promise 的状态和结果,无论成功或失败。手动实现Promise.all

现在我们来实现 Promise.all 的核心逻辑,帮助我们深入理解其工作原理。Promise.all 需要处理以下几种情况:

function myPromiseAll(promises) {
    return new Promise((resolve, reject) => {
        // 检查传入参数是否为数组
        if (!Array.isArray(promises)) {
            return reject(new TypeError('参数必须是数组'));
        }
        // 用于保存每个 Promise 的结果
        let results = [];
        // 记录已完成的 Promise 数量
        let computed = 0;
        // 总的 Promise 数量
        const total = promises.length;
        // 如果传入的数组为空,直接返回已解析的 Promise
        if (total === 0) {
            return resolve(results);
        }
        // 遍历每一个传入的 Promise
        promises.forEach((promise, index) => {
            // 使用 Promise.resolve 确保即使传入的不是 Promise 也能处理
            Promise.resolve(promise)
                .then(value => {
                    // 将每个 Promise 结果保存到对应的索引中
                    results[index] = value;
                    computed++;
                    // 当所有 Promise 都已解决,返回最终结果
                    if (computed === total) {
                        resolve(results);
                    }
                })
                .catch(error => {
                    // 如果有任何一个 Promise 被拒绝,立即返回失败的 Promise
                    reject(error);
                });
        });
    });
}

总结

到这里,我们已经实现了一个简单版的 Promise。通过自定义 MyPromise,我们了解了 Promise 的核心原理以及如何处理异步逻辑、状态管理和回调机制。希望这篇文章能够帮助大家更好地理解 Promise,在面试中轻松应对相关问题。如果这篇文章对你有帮助可以点个赞哦!


上一条查看详情 +在JS中,最初声明变量的关键字只有
下一条 查看详情 +没有了