前几天睿联的面试官问了这样一个问题:如何控制Promise.all的并发量,当时回答的不好,于是经过一段时间的探索,解决了该问题
首先我们要知道promise.all()的用法一般是是传入一个promise的数组,但是由于Promise的内部是同步代码,在Promise生成后其里面的异步请求就已发送,Promise.all()只是对其结果的一个收集,那么我们要想进行并发控制就必须在生成Promise的阶段进行并发控制下面是代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Promise.all()</title>
</head>
<body></body>
<script>
// 并发量
const limit = 3;
// 异步任务的参数数组,一般为url
const array = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1,
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2,
];
// 根据参数生成Promise的函数、一般为根据传入的url构造一个Promise,其内部封装一个异步请求
const proFn = (item) => {
return new Promise((resolve, reject) => {
console.log(`开始生成${item}的异步请求 ····`);
// 模拟异步请求
setTimeout(() => {
console.log(item);
// 得到结果
resolve(item);
console.log(`${item}的异步请求执行完毕 √`);
}, Math.random() * 1000);
});
};
console.log(promiseAll(array, proFn, limit));
// proFn返回一个封装了异步任务的promise
function promiseAll(arr = [], proFn, limit) {
// 当前正在遍历的坐标
let index = 0;
// 存放结果的数组
let res = [];
// 正在执行的数组
let excuting = [];
// 执行函数
function enqueue() {
// 当执行完毕之后返回resolve状态的promise
if (index === arr.length) {
return Promise.resolve();
}
// 依次取出一个元素
const item = arr[index++];
/* 此处then方法会立即返回一个promise,在then回调运行结束
(promise生成完毕)之后才会变成resolved状态,且当时的promise与
proFn生成的promise保持一致(1、状态一致;2、resolve或者reject的值一致)*/
const p = Promise.resolve().then(() => proFn(item, arr));
// 将其放到promise数组
res.push(p);
// 将e放入正在执行的数组,并且在p执行完成之后将当前执行的e删除掉
const e = p.then(() => {
excuting.splice(excuting.indexOf(e), 1);
});
excuting.push(e);
// 让r为一个默认resolved状态的promise
let r = Promise.resolve();
// 如果执行数组满了的话,那就让r通过race等待改变状态
if (excuting.length >= limit) {
r = Promise.race(excuting);
}
// 等到r变为resolved状态(执行数组没满或者有一个已经执行完被删除了)再来递归调用enqueue
return r.then(() => enqueue());
}
// 执行完成后,通过promise.all返回所有的结果
return enqueue().then(() => Promise.all(res));
}
</script>
</html>
运行结果:
开始生成1的异步请求 ····
开始生成2的异步请求 ····
开始生成3的异步请求 ····
2
2的异步请求执行完毕 √
开始生成4的异步请求 ····
1
1的异步请求执行完毕 √
开始生成5的异步请求 ····
3
3的异步请求执行完毕 √
开始生成6的异步请求 ····
4
4的异步请求执行完毕 √
开始生成7的异步请求 ····
5
5的异步请求执行完毕 √
开始生成8的异步请求 ····
6
6的异步请求执行完毕 √
开始生成9的异步请求 ····
7
7的异步请求执行完毕 √
开始生成10的异步请求 ····
8
8的异步请求执行完毕 √
开始生成11的异步请求 ····
10
10的异步请求执行完毕 √
开始生成12的异步请求 ····
9
9的异步请求执行完毕 √
开始生成13的异步请求 ····
11
11的异步请求执行完毕 √
开始生成14的异步请求 ····
12
12的异步请求执行完毕 √
开始生成15的异步请求 ····
15
15的异步请求执行完毕 √
开始生成16的异步请求 ····
13
13的异步请求执行完毕 √
开始生成17的异步请求 ····
14
14的异步请求执行完毕 √
开始生成18的异步请求 ····
17
17的异步请求执行完毕 √
开始生成19的异步请求 ····
16
16的异步请求执行完毕 √
开始生成20的异步请求 ····
18
18的异步请求执行完毕 √
开始生成1的异步请求 ····
19
19的异步请求执行完毕 √
开始生成2的异步请求 ····
20
20的异步请求执行完毕 √
开始生成3的异步请求 ····
1
1的异步请求执行完毕 √
开始生成4的异步请求 ····
4
4的异步请求执行完毕 √
开始生成5的异步请求 ····
3
3的异步请求执行完毕 √
开始生成6的异步请求 ····
2
2的异步请求执行完毕 √
开始生成7的异步请求 ····
6
6的异步请求执行完毕 √
开始生成8的异步请求 ····
5
5的异步请求执行完毕 √
开始生成9的异步请求 ····
8
8的异步请求执行完毕 √
开始生成10的异步请求 ····
7
7的异步请求执行完毕 √
开始生成11的异步请求 ····
9
9的异步请求执行完毕 √
开始生成12的异步请求 ····
12
12的异步请求执行完毕 √
开始生成13的异步请求 ····
10
10的异步请求执行完毕 √
开始生成14的异步请求 ····
11
11的异步请求执行完毕 √
开始生成15的异步请求 ····
13
13的异步请求执行完毕 √
开始生成16的异步请求 ····
15
15的异步请求执行完毕 √
开始生成17的异步请求 ····
16
16的异步请求执行完毕 √
开始生成18的异步请求 ····
18
18的异步请求执行完毕 √
开始生成19的异步请求 ····
14
14的异步请求执行完毕 √
开始生成20的异步请求 ····
17
17的异步请求执行完毕 √
开始生成1的异步请求 ····
19
19的异步请求执行完毕 √
开始生成2的异步请求 ····
1
1的异步请求执行完毕 √
20
20的异步请求执行完毕 √
2
2的异步请求执行完毕 √