手写 Promise.MyAll
的实现
手写Promise.all
是面试中的一道常考题,我们可以尝试手写一个类似的函数 Promise.MyAll
。以下是实现代码及其详细解释。
Promise.all()功能特点
在 JavaScript 中,Promise.all
是一个非常有用的方法,它允许我们同时处理多个 Promise
对象,并在所有 Promise
都成功完成时返回一个包含所有结果的数组。如果其中任何一个 Promise
失败,Promise.all
会立即返回失败的原因。
代码实现
// 在面试中注释的书写十分重要
/**
* 手写 Promise.MyAll 方法
* @param {Promise[]} promises - 一个包含多个 Promise 对象的数组
* @return {Promise} - 返回一个新的 Promise 对象
* @desc
* 1. 如果所有 Promise 都成功,返回一个包含所有结果的数组,结果的顺序与输入顺序一致。
* 2. 如果有一个 Promise 失败,立即返回失败的原因。
*/
Promise.MyAll = (promises) => {
// 用于存储每个 Promise 的结果
let arr = [],
count = 0; // 用于记录已完成的 Promise 数量
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
item
.then(res => {
arr[i] = res; // 将结果按顺序存入数组
count++;
// 如果所有 Promise 都已完成,则 resolve 结果数组
if (count === promises.length) {
resolve(arr);
}
})
.catch(err => {
reject(err)
})
});
});
};
代码解析
-
参数与返回值
promises
:一个包含多个Promise
对象的数组。- 返回值:一个新的
Promise
对象。如果所有Promise
都成功,则resolve
一个包含所有结果的数组;如果有一个Promise
失败,则reject
失败的原因。
-
核心逻辑
- 使用一个数组
arr
来存储每个Promise
的结果。 - 使用计数器
count
来记录已经完成的Promise
数量。 - 遍历
promises
数组,对每个Promise
调用then
方法:- 如果
Promise
成功,将结果按顺序存入arr
数组,并检查是否所有Promise
都已完成。如果是,则调用resolve(arr)
。 - 如果
Promise
失败,catch err。
- 如果
- 使用一个数组
-
注意事项
- 结果的顺序与输入
Promise
的顺序一致,这是通过将结果按索引存入数组实现的。 - 如果有一个
Promise
失败,Promise.MyAll
会立即返回失败的原因,而不会等待其他Promise
完成。
- 结果的顺序与输入
代码优化
我们可以使用到 .then 的第二个参数,也是一个函数,当 Promise 被拒绝(rejected)时调用,该函数接收拒绝的原因(通常是一个错误对象)作为其参数
Promise.MyAll = (promises) => {
// promises resolved 后的结果
let arr = [],
count = 0;
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
// 微任务
// then 两个参数 第一个是成功的回调 第二个是失败的回调
item
.then(res => {
arr[i] = res; // 按照顺序保存结果
count++;
if (count === promises.length) {
resolve(arr)
}
}, reject) // .then 的第二个参数 失败直接调用reject
// .cathch(err => {
// reject(err)
// })
})
})
}
示例测试
以下是一个使用 Promise.MyAll
的示例:
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ temp: 29, conditions: 'Sunny with Clouds' });
// reject('error'); // 可以测试失败的情况
}, 3000);
});
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I like cake, BBQ is ready now!');
// reject('BBQ 糊了'); // 可以测试失败的情况
}, 5000);
});
Promise
.MyAll([weather, tweets])
.then(res => {
console.log(res); // 输出所有 Promise 的结果
})
.catch(err => {
console.log(err); // 输出失败的原因
});
运行结果
-
所有
Promise
成功的情况:- 输出:
[ { temp: 29, conditions: 'Sunny with Clouds' }, 'I like cake, BBQ is ready now!' ]
- 输出:
-
有一个
Promise
失败的情况:- 如果
tweets
被reject
,输出:BBQ 糊了
- 如果
总结
通过手写 Promise.MyAll
,我们可以更深入地理解 Promise.all
的工作原理。其核心逻辑是通过遍历 Promise
数组,使用 then
方法监听每个 Promise
的状态,并在所有 Promise
都成功时返回结果数组,或者在某个 Promise
失败时立即返回失败原因。
这种实现方式不仅帮助我们理解了 Promise
的运行机制,还展示了如何通过计数器、数组索引等技巧来保证结果的顺序和正确性。