5分钟搞定Promise控制并发
起因
项目需要做一个上传功能,每次上传20个视频,不处理直接传的话会有一个阻塞问题。
因为一个网页最多同个域名只能有6个tcp连接。不控制并发同时传6个,用户后续的所有调其他接口的操作都会被挂起来。网络不好的情况下更是灾难,所以需要控制并发,一次最多只传3个。
正好又有这种类型的面试题,写个demo讲下原理。
后端代码
后端需要后整基本的返回,随机延迟,随机错误,能跨域
const http = require('http')
http.createServer((req,res)=>{const delay = parseInt(Math.random()*5000)
console.log(delay);
setTimeout(()=>{
const returnerror = Math.random()>0.8
// if(returnerror){
//res.writeHead(500,{
//// 'Content-Type': 'text/plain',
//'Access-Control-Allow-Origin':'*',
//'Access-Control-Allow-Headers':'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild',
//'Access-Control-Allow-Methods':'GET',
//}).end('error,from '+req.url+' after '+delay+'ms')
// }else{
res.writeHead(200,{
// 'Content-Type': 'text/plain',
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Headers':'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild',
'Access-Control-Allow-Methods':'GET',
}).end('OK,from '+req.url+' after '+delay+'ms')
// }},delay)
}).listen(3000,()=>{
console.log('服务启动在3000!');
})
前端代码 html主要为了动画的一个展示,就不细说了。
js分为两部分,一部分控制dom渲染,这个也不细说了,第二部分就是并发控制器
input数组里面是请求地址
文章图片
基础版
并发控制主要是
两个函数和一个promise队列
。- 一个负责不断的
递归
往队列里面加promise。 - 另外一个负责生产promise,处理异步请求逻辑
function handleFetchQueue(input, max) {
const requestsQueue = [];
// 请求队列
addReactive(requestsQueue) //对requestsQueue添加响应式
let i = 0;
const req = (i)=>{//产生一个promise请求 成功则删除队里中promise 再添加一个请求
returnfetch(input[i]).then(res=>res.text()).then(res=>{
addEl(res) //结果渲染页面列表
const index = requestsQueue.findIndex(item=>item===req)
requestsQueue.splice(index,1)
checkAddReq()
})
}
const checkAddReq = ()=>{
if(i>=input.length) return // 请求数不能越界
if(requestsQueue.length+1 <= max) { // 并发不能越界
setTimeout(()=>{ //加延迟为了提高动画效果,实际不需要
requestsQueue.push(req(i++))
checkAddReq()
},50)}
}
checkAddReq();
}
handleFetchQueue(input,3)
处理异常
需要考虑的问题是异常了怎么办。
异常对于并发控制也就是计数原理应该没有区别,只是需要额外再promise生成这里添加下业务逻辑即可。
我们把后端代码随机错误的注释打开
然后前端核心逻辑改一下
const req = (i)=>{
returnfetch(input[i]).then(async(res)=>{
if(res.status===500){ //这里异步改一下,得让走catch
const text = await res.text()
throw new Error(text);
}else{
return res.text()
}
}).then(res=>addEl(res),error=>addEl(error,true))
.finally(()=>{ //无论成功失败并发逻辑一样,只是then中业务逻辑不同
const index = requestsQueue.findIndex(item=>item===req)
requestsQueue.splice(index,1)
checkAddReq()
})
}
总结 完整地址 https://github.com/fyy92/code...
- index.html 不考虑异步
- index1.html 考虑异常
- serve.js 后端代码
- 并发控制关键在两个函数,一个控制生成业务Promise,一个迭代控制并发队列添加业务Promise。
- 对于异常,需要保证并发逻辑一样,不同的是业务逻辑的处理。
推荐阅读
- 使用Promise对微信小程序wx.request请求方法进行封装
- 从如何使用到如何实现一个Promise
- Promise详解
- Promise|Promise 异步控制流
- 告别无效忙碌、低效生活,“重要”“紧急”四象限法则轻松搞定
- 写作这件小事儿,我能搞定
- 5分钟,了解新媒体入门及岗位选择
- 二、“10堂课搞定自媒体写作课——韩大爷的杂货铺”上后感
- 一篇博文搞定goctl(V1.3.0新版本解决goctl|一篇博文搞定goctl:V1.3.0新版本解决goctl rpc protoc的问题)
- 86