手写Promise.all() 方法(前端面试真题)
文章目录
-
- promise简介
- 第一版
- 第二版
- 小结
promise简介
Promise 是JavaScript在ES6中引入的一种用于异步计算的对象。它代表了一个可能现在还没有结果,但将来某个时刻会有结果的值。Promise 的主要目的是提供一个更合理、更强大的方式来处理异步操作。
-
状态:一个
Promise对象有三种状态:-
Pending(等待中):初始状态,既不是成功,也不是失败状态。
-
Fulfilled(已成功):意味着操作成功完成。
-
Rejected(已失败):意味着操作失败。
-
-
创建:使用
new Promise构造函数来创建一个新的Promise对象。let promise = new Promise((resolve, reject) => { if(/*条件成立*/) { resolve() // 调用成功方法,状态改变为fulfilled } else { reject() // 调用失败方法,状态改变为rejected } }promise构造函数接受一个回调函数作为参数,回到函数会被注入resolve与reject两个函数作为参数,分别用于改变promise对象的两种状态。
值得注意的是,一但promise的状态由Pending切换为Fulfilled或Rejected之一后,将无法再次改变。同时promise的状态只有通过其构造函数来改变,在promise外部无法修改其状态。
3. 处理结果:使用 .then() 方法来添加在 Promise 成功时执行的回调函数,使用 .catch() 方法来添加在 Promise 失败时执行的回调函数。
promise .then(value => {
// 当 promise 成功时,这里的代码会被执行
console.log('Success:', value);
}) .catch(error => {
// 当 promise 失败时,这里的代码会被执行
console.error('Error:', error);
});
4.链式调用:.then() 方法返回一个新的 Promise,这允许你链式调用多个 .then() 和 .catch()。
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.catch(error => console.error(error));
5. Promise.all():用于处理多个 Promise 对象,只有当所有 Promise 都成功时,它才会成功。如果任何一个 Promise 失败,它就会失败。
Promise.all([promise1, promise2, promise3])
.then(([result1, result2, result3]) => {
// 所有 Promise 都成功时执行
}) .catch(error => {
// 任何一个 Promise 失败时执行
});
6. Promise.race():与 Promise.all() 类似,但是只要有一个 Promise 成功或失败,它就会立即执行。
第一版
Promise.all = Promise.all || function (promises) {
// 判断传入参数是否是数组
if(!Array.isArray*(promises)) {
throw new Error('Promise.all must accept an array'); // 如传入参数不是数组,抛出错误提示该函数必须传入一个数组作为参数
}
return new Promise((resolve,reject) => {
// 结果数组
const res = []
// 遍历参数
promises.forEach((item ,index)=> {
// 判断每个item是否是promise对象
if(!item instanceof Promise) {
reject(new Error('Promise.all must accept an array of promises')) // 如果不是promise对象,则抛出错误提示需要接受promise对象类型的数据
}
// 执行该对象
item.then(data => {
res[index] = data // 将执行结果存入结果数组中
index++ // 序号增加
// 若此时序号与传入参数长度一致,则说明已经遍历执行完所有的promise对象
if(index === promises.length) {
resolve(res) // 返回结果数组
}
},
reason => {
// 此时说明有promise对象执行失败,直接返回失败结果
reject(reason)
})
})
})
}
第二版
第二版中限制使用ES6相关的新特性,使用低版本实现。(面试官但还是限制使用const ,let,forEach等高级API)
Promise.all = Promise.all || function (promises) {
// 判断传入参数是否是数组
if(!Array.isArray(promises)) {
throw new Error('Promise.all must accept an array'); // 如传入参数不是数组,抛出错误提示该函数必须传入一个数组作为参数
}
return new Promise((resolve,reject) => {
// 结果数组
var res = []
for(var i = 0; i < promises.length; i++) {
// 由于promise是异步执行的,所有必须将每次的i通过函数参数的形式传递给then方法否则promise还未执行,i已经增加了
function handle(index) {
// 判断每个元素是否是promise对象
if(!promises[index] instanceof Promise) {
reject(new Error('Promise.all must accept an array of promises')) // 如果不是promise对象,则抛出错误提示需要接受promise对象类型的数据
}
promises[index].then(data => {
res[index] = data // 将执行结果存入结果数组中
index++ // 序号增加
// 若此时序号与传入参数长度一致,则说明已经遍历执行完所有的promise对象
if(index === promises.length) {
resolve(res) // 返回结果数组
}
},
reason => {
// 此时说明有promise对象执行失败,直接返回失败结果
reject(reason)
})
}
handle(i)
}
})
}
小结
主要差异
- 索引处理:方法二通过使用
handle函数和let声明避免了闭包问题,确保每个Promise都能正确地更新自己的索引位置。 - 错误处理:方法二在发现非
Promise对象时立即拒绝,而方法一则可能在循环结束后才抛出错误。 - 结果数组初始化:方法二使用
new Array(promises.length)来初始化结果数组,这有助于避免索引问题。
尽管两种方法都尝试实现 Promise.all 的功能,但方法二在处理异步操作和索引管理方面更为健壮和准确。然而,需要注意的是,这些自定义实现通常不应该用于生产环境,因为原生的 Promise.all 方法已经非常优化,并且经过了广泛的测试。自定义实现可能会引入难以预料的bug,特别是在处理复杂的异步逻辑时。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)