@author: 郭瑞峰
@createTime: 2023/09/26
@updateTime: 2023/09/26
哔哔啵啵的前言
不得不吐槽了这两天开发,开发三方系统的概览页面,概览页面最大的特点就是图表多,还有并发请求。当然,我那个页面也就要请求48个接口,也不。。。多??
卧槽,48个接口,后端呢?救我一下啊,后端还有200个bug(或者说是票)等着解决呢。
emmm,48个接口,玩我呢?
emmm,肯定不能一次性并发请求,不然要炸
但是,万一能行呢?
不出意料的崩了
第一步,我们宣称48个没有什么事儿,直接套用现成的接口并发请求。
一切都好,48个接口一个都没报错
第二次测试时候,三方服务器炸了
(有的时候要从服务器上面找原因好吧,这么多年都是这么卡,有没有认真工作,好不好)
没办法,只有老老实实地分流请求
解决方案
用我不太聪明的小脑瓜想了想,想到仨解决方案
修改 axios maxContentLength 属性
若是用 axios 库的话,可以修改maxContentLength属性值来限制并发请求,当超过上限时候就会进入等待(可能是等待队列,这个后续自己调研一下把)
import axios from 'axios'
const request = axios.create({
maxContentLength: 10
})
export default request
但最后否定了,原因在于axios已经进行过二次封装,不能随便更改(即使这个改动对其他模块无任何影响)
分流使用 Promise.all
注意:用的是Promise.all不是Promise.any
Promise.all是等所有数组resolve后执行then
Promise.any是数组有一个resolve后执行then
先上代码吧,可以用浏览器开发工具运行一下
const promise = (index) => new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() <= 0.5) {
resolve(`Promise ${index} run success`)
} else {
reject(`Promise ${index} get error`)
}
}, Math.random() * 1000)
})
// 举个50并发的例子
const list = new Array(50).fill((index) => promise(index))
const running = (index) => {
if (index >= list.length) return
Promise.all([
list[index](index),
list[index + 1](index + 1),
list[index + 2](index + 2),
list[index + 3](index + 3),
list[index + 4](index + 4)
])
.then((valueList) => {
console.log('Promise.all run successfully')
console.log(valueList)
console.log('\n')
})
.catch(error => {
console.log('Promise.all get error')
console.log(error)
console.log('\n')
})
.finally(() => {
running(index + 5)
})
}
running(0)
优点: 方便简单,通过分段方式解决了高并发请求
缺点也很明显:
- 同一批请求内要是有一个请求异常,其他同批次的请求就不会有后续操作
- 后续二次开发,迭代开发时候需要重新理一遍
Promise.all的then返回值的顺序
多个链式请求
链式请求的实质是等待上一个请求完成后执行下一个请求(这可能是我自创的定义,也许有其他名词形容,希望大佬们评论区教我做人)
还是老样子,先上代码
const promise = (index) => new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() <= 0.5) {
resolve(`Promise ${index} run success`)
} else {
reject(`Promise ${index} get error`)
}
}, Math.random() * 1000)
})
.then(data => {
console.log(data)
return list
})
.catch(error => {
console.log(error)
return list
})
const list = new Array(50).fill(index => promise(index))
const promise = (index) => new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() <= 0.5) {
resolve(`Promise ${index} run success`)
} else {
reject(`Promise ${index} get error`)
}
}, Math.random() * 1000)
})
.then(data => {
console.log(data)
return list
})
.catch(error => {
console.log(error)
return list
})
const list = new Array(50).fill(index => promise(index))
const running = () => {
let count = 0
const worker = () => {
if (count >= list.length) return // 设置退出条件
list[count](count++)
.then(() => {
worker()
})
}
// 设置五个链
worker()
worker()
worker()
worker()
worker()
}
running()
在每个Promise中的then和catch中return并发请求对象,这样当前接口执行完成后就可以继续执行then,然后再then中调用对应接口(这边建议使用对象方式调用,方便阅读和迭代开发)
优点:
- 每个请求以及后续处理相互独立,互不干扰
- 通过设置多个链式来削减并发请求数量
- 方便溯源接口
缺点很明显:
- 每个接口都要写后续操作以及异常处理,最后都要返回接口列表/对象
- 每个链式都要设置好(规划好)请求链路
- 会出现单链忙碌,其他链围观(接口简单,老早就摸鱼了)
最终
考虑再三,咱最终使用链式请求,很意外吧,居然不用Promise.all的方法
那么说一下为啥咱用链式请求,在于这是概览页面,每个接口后续处理必须相互独立。
若是有啥好的建议欢迎大佬们在评论区说一下