cause
The project needs to do an upload function, upload 20 videos at a time, and there will be a blocking problem if the direct upload is not processed.
Because a webpage can only have 6 tcp connections for the same domain name at most. It does not control the concurrent transmission of 6 at the same time, but all subsequent operations of the user to call other interfaces will be suspended. It is even a disaster when the network is not good, so it is necessary to control the concurrency, and only transfer at most 3 at a time.
It happens that there is another interview question of this type, so write a demo to explain the principle.
backend code
The backend needs to have basic returns, random delays, random errors, and cross-domain
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!');
})
front-end code
HTML is mainly for a display of animation, so I won't go into details.
js is divided into two parts, one part controls dom rendering, see the source code address summed up, this is not detailed, the second part is the concurrent controller
The input array is the request address
const input = new Array(10).fill('http://localhost:3000/').map((item,index)=>`${item}${index+1}`)
Basic Edition
Concurrency control is mainly two functions and a promise queue.
- One is responsible for the constant
recursively adding promises to the queue.
- The other is responsible for producing promises and handling asynchronous request logic
function handleFetchQueue(input, max) {
const requestsQueue = []; // 请求队列
addReactive(requestsQueue) //对requestsQueue添加响应式,重写push,splice方法使其能同时改变页面上的队列dom。
let i = 0;
const req = (i)=>{ //产生一个promise请求 成功则删除队里中promise 再添加一个请求
return fetch(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)
Handling exceptions
The question to be considered is what to do if there is an exception.
There should be no difference between exceptions for concurrency control, that is, the counting principle, just need to generate additional promises and add business logic here.
We turn on the comments of random errors in the backend code
Then change the front-end core logic
const req = (i)=>{
return fetch(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()
})
}
Summarize
Full address https://github.com/fyy92/codeTraining/tree/master/21.Promise
- index.html does not consider async
- index1.html considers exceptions
- serve.js backend code
in conclusion
- The key to concurrency control lies in two functions, one to control the generation of business promises, and the other to iteratively control the concurrent queue to add business promises.
- For exceptions, it is necessary to ensure that the concurrency logic is the same, but the difference is the processing of business logic.
Replenish
Recently I found a library asyncpool to imitate it
const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
const results = await asyncPool(2, [1000, 5000, 3000, 2000], timeout);
// Call iterator (i = 1000)
// Call iterator (i = 5000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 1000 finishes 1秒后
// Call iterator (i = 3000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 3000 finishes 4秒后
// Call iterator (i = 2000)
// Itaration is complete, wait until running ones complete...
// 5000 finishes 5秒后
// 2000 finishes 6秒后
// Resolves, results are passed in given array order `[1000, 5000, 3000, 2000]`.
The online version generally uses Promise.settle Promise.all
So for example generate a full Promse array
And what we introduced earlier is to generate Promise in stages
So at night, it is equivalent to encapsulating a layer of functions for the operation we generate promises, and then we can use Promise.settle Promise.all. We use the principle of Promise.settle Promise.all directly here or do it according to the previous idea
unction asyncPool(poolLimit, array, iteratorFn){
return new Promise((resolve,reject)=>{
const queue =[]
const result = []
let num = 0
let i = 0
const add = ()=>{
// console.log(i,queue.length)
if(i>=array.length) return
if(queue.length>=poolLimit) return
const p = iteratorFn(array[i]).then((res)=>{ //这里最好用finally 但是注意finally res为undefined
// console.log(i)
result.push(res)
queue.splice(queue.findIndex(i=>i===p),1)
add(i) //i已经加过了被return了的
num++
console.log(num)
if(num===array.length){
resolve(result)
}
})
queue.push(p)
add(++i)
}
add(0)
})
}
let oldtime = new Date().getTime()
const timeout = i => new Promise(resolve => setTimeout(() => {
resolve(i)
// console.log(i,new Date().getTime()-oldtime)
}, i));
asyncPool(2, [1000, 5000, 3000, 2000], timeout).then(res=>{
console.log(res) //1000 3000 5000 2000
})
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。