前端

实现 Promise.all 的关键点

当我们实现 Promise.all 方法时,有几个关键点需要注意,以确保其正确性和兼容性。以下是一些重要的考虑因素:

  • 输入类型检查Promise.all 接受一个可迭代对象(通常为数组)作为参数。首先需要验证传入的参数是否为可迭代类型。如果不是,应该抛出一个 TypeError

  • 返回值Promise.all 返回一个新的 Promise 对象,该对象在所有传入的 promise 全部解决或任何一个 promise 被拒绝后解决或拒绝。

  • 并行处理:所有的 promise 是并行执行的,而不是按顺序。这意味着不应该使用任何阻塞操作来等待每个 promise 解决。

  • 结果顺序:即使传入的 promise 以不同的顺序解决,返回的结果数组中的元素也必须与传入数组中的 promise 顺序一致。因此,需要在内部维护一个用于跟踪结果顺序的数据结构。

  • 错误处理:如果其中任何一个 promise 拒绝,整个 Promise.all 调用就会立即拒绝,并携带第一个被拒绝的错误信息。这要求对每个单独的 promise 设置错误处理机制。

  • 异步行为:确保最终返回的新 Promise 对象是异步决议的,即使所有输入 promises 都已经完成,这也是 JavaScript 事件循环和微任务队列运行机制的一部分。

下面是一个简单实现示例:

function myPromiseAll(iterable) {
    return new Promise((resolve, reject) => {
        if (typeof iterable[Symbol.iterator] !== 'function') {
            return reject(new TypeError(`${typeof iterable} is not iterable`));
        }
 
        const results = [];
        let remaining = iterable.length;
 
        if (remaining === 0) {
            return resolve(results);
        }
 
        const processResult = (index, value) => {
            results[index] = value;
            remaining -= 1;
            if (remaining === 0) {
                resolve(results);
            }
        };
 
        iterable.forEach((promise, index) => {
            Promise.resolve(promise).then(
                value => processResult(index, value),
                reason => reject(reason)
            );
        });
    });
}

这个实现示例展示了如何使用基本逻辑来模拟 Promise.all 的行为。通过将所有输入 promises 转换为已解决状态,我们可以更轻松地管理它们,无论它们是已经解决、尚未解决还是被拒绝。同时,这种方法也能确保即使输入中含有非 Promise 值时,函数也能正常工作,因为 Promise.resolve() 会自动将这些值转换为已解决状态的 Promise。