0
点赞
收藏
分享

微信扫一扫

深度解析Promise.all的核心功能并手写实现


在之前的文章中,已经手写实现了​​Promise​​​的核心功能,包括​​resolve​​​,​​reject​​​,​​then​​​。​​Promise​​​还有一些拓展方法,比如​​Promise.all​

在手写实现一些原生提供的方法时,第一步要做的事情就是先了解这个方法的使用过程和基本原理。

所以我们先了解一下​​Promise.all​​做的事情。

原生Promise.all的使用

以下是MDN文档的简述:


​Promise.all()​​​ 方法接收一个​​promise​​​的​​iterable​​​类型(注:​​Array​​​,​​Map​​​,​​Set​​​都属于​​ES6​​​的​​iterable​​​类型)的输入,并且只返回一个​​Promise​​​实例, 那个输入的所有​​promise​​​的​​resolve​​​回调的结果是一个数组。这个​​Promise​​​的​​resolve​​​回调执行是在所有输入的​​promise​​​的​​resolve​​​回调都结束,或者输入的​​iterable​​​里没有​​promise​​​了的时候。它的​​reject​​​回调执行是,只要任何一个输入的​​promise​​​的​​reject​​​回调执行或者输入不合法的​​promise​​​就会立即抛出错误,并且​​reject​​的是第一个抛出的错误信息。


简单来说就是批量化处理​​promise​​​实例以一个数组或者类数组的方式存储,当所有的​​promise​​​的​​resolve​​​都执行完成,或者存在任意一个​​promise​​​的​​reject​​被执行,都立即返回。

要么全成功,一起返回,只要有一个失败,就立即返回当前失败的内容。

下面我们简单用一下。

let promise1 = new Promise((resolve, reject) => {
resolve(1);
})
let promise2 = new Promise((resolve, reject) => {
resolve(2);
})
let promise3 = new Promise((resolve, reject) => {
resolve(3);
})

Promise.all([promise1, promise2, promise3])
.then(res => {
console.log(res);
}, err => {
console.log(err);
})

深度解析Promise.all的核心功能并手写实现_开发语言

输出的结果如上所示。

我们知道原生​​Promise.all​​的基本原理和使用方法之后,试着来自己实现以下。

手写实现Promise.all

Promise.all = function(promises) {
let results = [];
let length = promises.length;
let promiseCount = 0;
return new Promise((resolve, reject) => {
for (let i of promise1) {
Promise.resolve(i).then(res => {
results.push(res);
promiseCount ++;

if (promiseCount === length) {
resolve(results);
}
}, err => {
reject(err);
})
}
})
}

简单解释一下上面的代码,首先函数接收一个参数​​promises​​​,代表保存了很多​​promise​​实例的数组或类数组。

​results​​​是用来存储每个​​promise​​​实例执行之后的结果。因为最后要以数组的方式将所有​​promise​​的执行结果返回。

​promiseCount​​​是记录​​promise​​​的执行次数,当所有的参数中所有的​​promise​​​实例都执行完成,就将​​results​​返回。

接下来,​​return​​​一个新的​​promise​​​实例。在官方文档中有说明,​​Promise.all​​​方法最终会返回一个​​Promise​​实例。

在构造函数中的处理,循环接收到的 ​​promises​​​参数,挨个执行,需要注意的一点是,避免使用者传入非​​Promise​​​类型的元素,所以在遍历的时候将每个元素都用​​Promise.resolve​​包裹一下。

接下来的操作就是将当前遍历的​​promise​​​实例的​​resolve​​​执行结果添加到​​results​​​数组中,​​promiseCount ++​​​,代表当前实例执行完了。如果​​promiseCount === length​​​就代表,所有​​promise​​​元素都执行完了。返回存储所有​​promise​​​执行结果的​​results​​数组即可。

如果有任意一个​​promise​​​元素执行的是​​reject​​​方法,立即结束当前循环,执行​​reject​​方法,返回报错信息。

其实以上源码可以实现基本功能,但是还有点瑕疵。在给往​​results​​​数组中​​push​​​当前遍历的​​promise​​​执行结果时,其实不准确。因为​​promise​​​实例中处理的可能是异步事件,大部分也都是异步事件。直接​​push​​并不准确。

比如传入的​​promise​​​的​​resolve​​​输出是​​[1, 2, 3]​​​; 如果第一个​​promise​​​实例中存在异步事件,比如加了一个定时器,按照当前的写法,直接 ​​push​​​的话​​Promise.all​​​返回的一定是​​[2, 3, 1]​​; 这其实是不对的。应该按照顺序返回。

用原生的​​Promise.all​​试一下。

let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
})
})
let promise2 = new Promise((resolve, reject) => {
resolve(2);
})
let promise3 = new Promise((resolve, reject) => {
resolve(3);
})

Promise.all([promise1, promise2, promise3])
.then(res => {
console.log(res);
}, err => {
console.log(err);
})

深度解析Promise.all的核心功能并手写实现_开发语言_02

与猜测一致。无论返回的携带有​​promise​​执行结果的数组中的顺序应该与传入时的顺序一致。

优化版的源码如下:

Promise.all = function(promises) {
let results = [];
let length = promises.length;
let promiseCount = 0;
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(res => {
results[i] = res;
promiseCount ++;

if (promiseCount === length) {
resolve(results);
}
}, err => {
reject(err);
})
}
})
}

解决方案其实就是,通过当前遍历传入参数的索引来作为​​results​​的索引,往里面添加。

这样就一定是按输入的顺序返回。

欢迎大家关注我的公众号,有很多关于前端的内容哦
公众号:Code程序人生
B站账号:LuckyRay123
个人博客:http://rayblog.ltd/



举报

相关推荐

0 条评论