背景

接手了一个老项目,启动后首页打开请求三十几个接口,人都崩溃。只能想办法写promise优化下,现在基本上前端项目都是通过axios来实现异步请求的封装,异步编程。

明确概念

这里有几个概念需要明确一下

  • 并发:并发是多个任务同时交替的执行(因为cpu执行指令的速度非常之快,它可以不必按顺序一段代码一段代码的执行,这样效率反而更加低下),这样看起来就是一起执行的,所以叫并发。
  • 并行:可以理解为多个物理cpu或者有分布式系统,是真正的’同时’执行
  • 并发控制:意思是多个并发的任务,一旦有任务完成,就立刻开启下一个任务
  • 切片控制:将并发任务切片的分配出来,比如10个任务,切成2个片,每片有5个任务,当前一片的任务执行完毕,再开始下一个片的任务,这样明显效率没并发控制那么高了

思路

首先执行能执行的并发任务,根据并发的概念,每个任务执行完毕后,捞起下一个要执行的任务。

将关键步骤拆分出合适的函数来组织代码

  1. 循环去启动能执行的任务
  2. 取出任务并且推到执行器执行
  3. 执行器内更新当前的并发数,并且触发捞起任务
  4. 捞起任务里面可以触发最终的回调函数和调起执行器继续执行任务
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
// 设计一个函数,可以限制请求的并发,同时请求结束之后,调用callback函数
// sendRequest(requestList:,limits,callback):void
sendRequest(

[() => request('1'),

() => request('2'),

() => request('3'),

() => request('4')],

3, //并发数

(res) => {

console.log(res)

})

// 其中request 可以是:
function request(url, time = 1) {

return new Promise((resolve, reject) => {

setTimeout(() => {

console.log('请求结束:' + url);

if (Math.random() > 0.5) {

resolve('成功')

} else {

reject('错误;')

}

}, time * 1e3)

})
}
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
async function sendRequest(requestList,limits,callback){

// 维护一个promise队列

const promises = []

// 当前的并发池,用Set结构方便删除

const pool = new Set() // set也是Iterable<any>[]类型,因此可以放入到race里

// 开始并发执行所有的任务

for(let request of requestList){

// 开始执行前,先await 判断 当前的并发任务是否超过限制

if(pool.size >= limits){

// 这里因为没有try catch ,所以要捕获一下错误,不然影响下面微任务的执行


await Promise.race(pool)

.catch(err=>err)

}

const promise = request()// 拿到promise

// 删除请求结束后,从pool里面移除

const cb = ()=>{

pool.delete(promise)

}

// 注册下then的任务

promise.then(cb,cb)

pool.add(promise)

promises.push(promise)

}

// 等最后一个for await 结束,这里是属于最后一个 await 后面的 微任务

// 注意这里其实是在微任务当中了,当前的promises里面是能确保所有的promise都在其中(前提是await那里命中了if)


Promise.allSettled(promises).then(callback,callback)

}