Axios JS Maximum Concurrent Requests

Matt Hue
3 min readFeb 10, 2019

--

Imagine you have a re-usable Vue component that fetches data from a backend service and displays the results on your webpage.

Something like:

// KPI.vueimport axios from 'axios'export default {  
props: ['id'],
mounted() {
axios.get('/api/kpis/${this.id}')
.then(response => {
this.kpi = response.data.kpi
})
},
}

And in your templates, you use your kpi component like so:

<kpi id="1">{{ kpi }}</kpi>

Everything’s fine, you reload the page, kpi is displayed and you pat your self in the back for a job well-done.

Now, client or project manager comes back to you and say “hey lets display all the kpi’s in one page in a list, without pagination”.

But you know the api server only accepts 5 concurrent connections at any given time and you have around 100 kpi’s and growing. How could you solve this problem?

Enter Promise.all()

You thought, ok we can solve it using Promise.all() and resolve all 5 requests at a time.

let req = [
axios.get('/api/kpis/1'),
axios.get('/api/kpis/2'),
axios.get('/api/kpis/3'),
axios.get('/api/kpis/4'),
axios.get('/api/kpis/5'),
];
Promise.all(req).then((req1, req2, req3, req4, req5) => {});

The problem with this approach is that when any of the requests fails, all of the promises inside all() is rejected. So when say req2 fails, the Promise.all() promise rejects the entire requests. There should be a better way.

Enter Axios Interceptors

Luckily for us, axios provides request interceptors for these kinds of things. We can create a response/request interceptor and we can do anything with it.

api.interceptors.request.use(function (config) {
console.log(`Here is the request data before the request is sent: ${config}`);
return config;
})

And we can do the same for response interceptors.

Now the good thing about interceptors is that you can return a Promise! How cool is that!

With a promise, we can do any async/sync jobs before a request is sent or before a response is resolved. Back to the original maximum concurrency problem, we can create somewhat of a counter inside the interceptors that tracks all the requests being sent to the server.

import axios from 'axios'const MAX_REQUESTS_COUNT = 5
const INTERVAL_MS = 10
let PENDING_REQUESTS = 0
// create new axios instance
const api = axios.create({})
/**
* Axios Request Interceptor
*/
api.interceptors.request.use(function (config) {
return new Promise((resolve, reject) => {
let interval = setInterval(() => {
if (PENDING_REQUESTS < MAX_REQUESTS_COUNT) {
PENDING_REQUESTS++
clearInterval(interval)
resolve(config)
}
}, INTERVAL_MS)
})
})
/**
* Axios Response Interceptor
*/
api.interceptors.response.use(function (response) {
PENDING_REQUESTS = Math.max(0, PENDING_REQUESTS - 1)
return Promise.resolve(response)
}, function (error) {
PENDING_REQUESTS = Math.max(0, PENDING_REQUESTS - 1)
return Promise.reject(error)
})
export default api

You can view the gist here. You can copy the code above and put into a separate file and import it to any requests where you need concurrency limitation!

On the first few lines, we imported axios and declared constants that will hold values for our concurrency limiter. MAX_REQUESTS_COUNT is the maximum number of requests at any given time.

INTERVAL_MS is the interval time to check of there are vacant slots in the request queue. PENDING_REQUESTS holds the value for the current number of requests being dispatched.

And we clone a new axios instance so that we don’t mess with the global axios object. The request interceptor we had just returns a promise and inside that promise is a setInterval function. The interval function just checks if there is enough room to send another request and increment the PENDING_REQUESTS counter.

The response interceptor, on the other hand tracks any request response (including failed ones) and decrement the PENDING_REQUESTS counter once a new request is completed.

Now you can render your kpi component infinitely without worrying about bogging down your server because you have 100 kpi’s loaded all at once!

<kpi id="1">{{ kpi }}</kpi>
<kpi id="2">{{ kpi }}</kpi>
<kpi id="3">{{ kpi }}</kpi>
...
<kpi id="97">{{ kpi }}</kpi>
<kpi id="98">{{ kpi }}</kpi>
<kpi id="99">{{ kpi }}</kpi>

That’s all.

--

--