手写场景 | Promise实现并发数控制

需求

实现一个输入url数组与并发数的并发数量,控制请求队列函数

核心思路

控制并发数量,关键点是利用promise,当一个请求完成之后再去发起下一个请求

  • 要完成一个继续一个,保证同时有limit个在同时执行,所以需要一个递归的执行函数run(用于执行并发limit个url)
  • 首先在函数执行时,需要将执行中的任务按顺序填满,数量为限制并发数(利用for循环执行)
  • 执行函数中,需要在执行完成后,判断是否有下一个待执行任务:(如果执行完毕的数量小于urls的长度,说明还没有执行完)
    • 声明变量i来计数,与最终需要执行的总数比较判断是否需要递归执行下一个任务

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// url执行函数(充当着发送请求的任务)
const fn = (url) => {
// 这里用Promise来模拟发送请求
return new Promise((resolve, reject) => {
console.log('完成一个任务', url, new Date());
resolve({
url,
date: new Date(),
})
})
}


// 控制并发url函数
/*
传入参数为:
1. urls[数组]
2. limit[允许并发数]
*/
function limitQueue(urls, limit) {
// 完成任务数量(记录)
let fulfilledIndex = 0;
// 填充执行队列
for (let executeIndex = 0; executeIndex < limit; executeIndex++) {
run(); // 执行任务
};

// 执行任务函数
function run() {
// 构造待执行任务,当该任务完成后,如果还有待完成得任务 就继续执行
new Promise((resolve, reject) => {
let url = urls[fulfilledIndex++]; // 获取当前url
resolve(fn(url)); // resolve fn执行得结果
}).then(() => {
if (fulfilledIndex < urls.length) { // 说明还有没执行完的
run();
}
})
}
}

// test
const urls = [1,2,3,4,5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];

(async _ => {
await limitQueue(urls, 4);
})();


/*
完成一个任务 1 2022-07-16T07:23:41.283Z
完成一个任务 2 2022-07-16T07:23:41.287Z
完成一个任务 3 2022-07-16T07:23:41.288Z
完成一个任务 4 2022-07-16T07:23:41.288Z
完成一个任务 5 2022-07-16T07:23:41.288Z
完成一个任务 6 2022-07-16T07:23:41.289Z
完成一个任务 7 2022-07-16T07:23:41.289Z
完成一个任务 8 2022-07-16T07:23:41.289Z
完成一个任务 9 2022-07-16T07:23:41.289Z
完成一个任务 10 2022-07-16T07:23:41.289Z
完成一个任务 11 2022-07-16T07:23:41.289Z
完成一个任务 12 2022-07-16T07:23:41.289Z
完成一个任务 13 2022-07-16T07:23:41.289Z
完成一个任务 14 2022-07-16T07:23:41.289Z
完成一个任务 15 2022-07-16T07:23:41.290Z
*/