The scene is introduced into
There are 100 asynchronous requests that need to be sent, but for some reason we need to limit the number of concurrent requests to 10 at a time and get the response as quickly as possible. What should we do?
Stop reading and think about it. It’s an interesting question.
Thought analysis
My first thought was to shard the request, each within ten pieces, followed by promise.all (). But it quickly became clear that there was a wait time between each set of promises.all, which was not the fastest way.
The quickest way to do this would be to send ten requests at a time and send the next request as soon as one of them responds successfully. This is obviously a recursive process and we should do it in the callback function.
The specific implementation
First let’s implement this recursive function:
function next(){
// Handle recursion to the bottom... axios. Get (url). Then (res= > {
// Save the result...if (/* Request complete */) {
// Return the result...}else {
next() / / recursion
}
}).catch(/* Error handling */)}Copy the code
This allows for successive calls, but we also need to collect the data in order, so we need some extra variables to hold the state. Here is a complete Ajax function:
const axios = require('axios')
const arr = new Array(100).fill('https://www.baidu.com')
/** * @param {Array
} arr Request address * @param {Number} n Control the Number of concurrent */
function ajax (arr, n) {
const { length } = arr
const result = []
let flag = 0 // Control the progress, indicating the current position
let sum = 0 // Record the total number of requests completed
return new Promise((resolve, reject) = > {
// The maximum number of concurrent calls
while (flag < n) {
next()
}
function next(){
const cur = flag++ // Use closures to save the current location so that results can be stored sequentially
if (cur >= length) return
const url = arr[cur]
axios.get(url).then(res= > {
result[cur] = cur // Save the result. For order, the index values are saved here
if (++sum >= length) {
resolve(result)
} else {
next()
}
}).catch(reject)
}
})
}
ajax(arr, 10).then(res= > {
console.log(res)
})
Copy the code
The result is as follows:
[
0.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.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99
]
Copy the code
Thinking extension
In addition to concurrent requests, this problem extends to many scenarios. For example, file reading, if there are 100 files, generally will not read in one time.
Error logging
Halfway through, I thought of sliding Windows and wrote the following code:
const axios = require('axios')
const arr = new Array(100).fill('https://www.baidu.com')
function ajax (arr) {
let cur = 0 // The current position
let size = 0 // Window size
const result = []
while(arr.length ! = =0) {
if (size > 10) continue
size++
const url = arr.shift()
axios.get(url).then(res= > {
result[cur] = cur
size--
cur++
})
}
return result
}
ajax(arr)
Copy the code
Whether this code is correct or not is irrelevant. My idea is to loop over and over again, checking the window size to see if a request is made. Here’s the problem with this code.
JavaScript is based on an event loop, and each round of the event loop completes the macro task before emptying the microtask. Promise.then is a microtask, which means that the macro task will not be executed until the end of the current round.
In this code, the continue is consistently triggered after the first ten times the while executes. The reason is that the while loop is a macro task, which has not been completed yet. As a result, the micro-tasks registered by the THEN method have no chance to execute, so the size variable cannot be reduced, resulting in an infinite loop.
Here is a record of the wrong way of thinking, but also hope that you take warning.
If there are other implementations, feel free to leave a comment in the comments section.