javascript中「并发请求」的场景
前言
并发请求是面试中的高频问题,同时也是项目中经常会遇到的问题,所以在这里记录一下,希望可以帮助到你。本文主要从以下几个方面进行展开:
- 在本地使用
node创建并启动一个服务模拟项目中真实开发场景 - 并发请求场景分析
- 并发请求代码实现
创建并启动服务
1. 创建一个 server 文件夹📁,在终端中 npm init -y

2. 使用 yarn add express 下载 express 框架

3. 修改 package.json,并在 server 目录下创建 app.js 文件
"scripts": {
"dev": "nodemon ./app.js"
},
4. 启动服务 npm run dev ,如果报错:则下载 sudo npm install -g --force nodemon 重新 npm run dev

5. 编写 app.js 文件
// 引入express中间件
const express = require('express');
// 创建web服务器
const app = express();
// 跨域处理
app.all("*", (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'GET')
next()
})
//请求
for (let i = 0; i < 100; i++) { //最大请求接口数是100个
app.get('/test' + i, (req, res) => {
res.send({
result: `请求成功:请求的接口是第${i}`
})
})
}
// 启动服务器监听8000端口
app.listen(8000, () => {
console.log('启动成功');
})
6. 在根目录下新建一个 index.html 文件用于引入 axios。

7. 在index.html 文件中使用的是 cdn 的形式引入 axios。
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.min.js"></script>
<script type="module" src="./index.js"></script>
并发请求场景分析
所谓并发请求,首先需要考虑并发数,并发数就是一次最多可以发送多少个请求,假设现在有 10 个 url 需要发送请求,控制并发数是 3 ,那么就是一次最多可以发送 3 个,第一次时候拿出三个。如下:

🤔这里我们需要思考五个问题。
* 第一个问题:如何每次都可以取到三个 URL 去发送请求?
这里我们可以把请求放入到一个函数中,循环三次,当每次循环这个函数的时候就会发送一次请求。
function runRequest(){
//发送请求
}
for (let i = 0; i < 3; i++) {
runRequest()
}
* 第二个问题:我怎么知道当前我请求的是第几个?
我们知道请求的地址是一个数组,这里可以指定
index ===0,第一次请求的时候拿到的是数组的第一项,后面调用runRequest让index++,这样每次请求 runRequest 都会依次从数组中取出请求地址进行请求。
let index = 0 // 用于指定当前请求的那个
async function runRequest() {
const url = URLS[index]
index++
await axios(url)
}
for (let i = 0; i < 3; i++) {
runRequest()
}
* 第三个问题: 我是等待当前三个都请求完成以后再取出三个请求,还是等待三个中的一个请求完成以后再取出一个进行请求呢?
第一种情况需要等待上一轮的三个请求完成才会进行下一轮的请求,假如其中一个请求耗时比较长,那么仍然需要等待,从响应的时间的角度来说的话,第二种更好,第二种可以保证当前始终是3个请求。所以在上面的基础上我们可以这样做。
let index = 0 // 用于指定当前请求的那个
async function runRequest() {
const url = URLS[index]
index++
try {
await axios(url)
} catch (error) {
// 错误处理
} finally {
runRequest() //当前三个中有一个请求完成,调用自己添加一个请求
}
}
for (let i = 0; i < 3; i++) {
runRequest()
}
* 第四个问题:当前的 URL1,URL2,URL3中请求的结果如何正确的存放在res1,res2,res3中,可以使用 push 吗?
答案是不可以的,因为无法控制他们三个中谁先请求完成,假如
URL2请求先完成,返回的结果push到一个数组中,那个整个请求返回的结果顺序就会发生错误。解决办法是当每一次 index++之前都将index保存在一个变量 i 中,当一个请求完成之后它对应的 i 不会发生改变,这样就可以将它存放在对应的位置。
const Results = [] // 存放请求返回的结果
let index = 0 // 用于指定当前请求的那个
// 处理请求
async function runRequest() {
let i = index
const url = URLS[index]
index++
try {
const res = await axios(url)
Results[i] = res
} catch (error) {
Results[i] = error
} finally {
runRequest()
}
}
for (let i = 0; i < 3; i++) {
runRequest()
}
* 第五个问题:如果并发数量比接口数量大,假如只有5个接口,但是并发数量是10个,怎么处理?
let minNum = Math.min(URLS.length, maxNum)
for (let i = 0; i < minNum; i++) {
runRequest()
}
并发请求代码实现
这里请求主要有三个文件:分别是index.html,index.js和 EruptRequest.js。值得注意的是⚠️,这里的使用的模块化语法,打开 index.html 的时候需要用服务。

index.html代码:
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.min.js"></script>
<script type="module" src="./index.js"></script>
</body>
index.js完整代码:
import { dealtRequest } from "./EruptRequest.js";
// 遍历拿到 url 的集合
function getStack(num) {
const BASE_URL = 'http://localhost:8000/'
let stacks = []
for (let i = 0; i < num; i++) {
stacks.push(BASE_URL + 'test' + i)
}
return stacks
}
let URLS = getStack(10) //请求总接口数
let maxNum = 3 //请求最大并发数
let result = dealtRequest(URLS, maxNum)
console.log(result);
EruptRequest.js完整代码:
//处理并发请求
export function dealtRequest(URLS, maxNum) {
const Results = [] // 存放请求返回的结果
let index = 0 // 用于指定当前请求的那个
// 处理请求
async function runRequest() {
//这里要控制 index 的长度,如果index++ 一直增加就会造成死循环
if (URLS.length === index) return
let i = index //
const url = URLS[index]
index++
try {
const res = await axios(url)
Results[i] = res
} catch (error) {
Results[i] = error
} finally {
runRequest()
}
}
// 比较并发数和接口的数量,防止出现只有5个接口,并发数10的情况
let minNum = Math.min(URLS.length, maxNum)
for (let i = 0; i < minNum; i++) {
runRequest()
}
// 这个返回的是一个成功的 promise 对象 ,用于存储最后的请求结果
return new Promise((reslove) => {
reslove(Results)
})
}
转载自:https://juejin.cn/post/7236634852828217399