Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Throttling Axios Requests

I'm using axios to make requests to the Deezer API. Unfortunately, with Deezer's API when you request an artist's albums it does not include album tracks. So, I am working around this by requesting the artist's albums and then performing a subsequent axios request for each album. The problem I'm running into is that the API limits requests to 50 per 5 seconds. If an artist has more than 50 albums I usually get a "quota exceeded" error. Is there a way to throttle axios requests to 50 per 5 seconds, specifically when using axios.all?

var axios = require('axios');

function getAlbums(artistID) {
  axios.get(`https://api.deezer.com/artist/${artistID}/albums`)
    .then((albums) => {
      const urls = albums.data.data.map((album) => {
        return axios.get(`https://api.deezer.com/album/${album.id}`)
          .then(albumInfo => albumInfo.data);
      });
      axios.all(urls)
        .then((allAlbums) => {
          console.log(allAlbums);
        });
    }).catch((err) => {
      console.log(err);
    });
}

getAlbums(413);
like image 338
Spencer Charest Avatar asked Apr 18 '17 21:04

Spencer Charest


2 Answers

Here is my solution using simple async setTimeout / Es6 code :

you can setup the delay in the sleep param func

const sleep = (delay) => {
  return new Promise(function(resolve) {
    setTimeout(resolve, delay);
  });
}


axios.interceptors.response.use(async function (response) {
    await sleep(3000)
    return response;
  }, function (error) {
    // Do something with response error
    console.error(error)
    return Promise.reject(error);
  });

There is also this npm package you can use : https://www.npmjs.com/package/axios-request-throttle

like image 142
Loic H Avatar answered Nov 19 '22 05:11

Loic H


First of all, let's see what you really need. Your goal here is to make request at most each 100 milliseconds, if you have a large number of albums. (Using axios.all for this matter is no different from using Promise.all, you just want to wait for all of the requests to complete.)

Now, with axios you have the interception API, allowing to plug your logic before requests. So you can use an interceptor like this:

function scheduleRequests(axiosInstance, intervalMs) {
    let lastInvocationTime = undefined;

    const scheduler = (config) => {
        const now = Date.now();
        if (lastInvocationTime) {
            lastInvocationTime += intervalMs;
            const waitPeriodForThisRequest = lastInvocationTime - now;
            if (waitPeriodForThisRequest > 0) {
                return new Promise((resolve) => {
                    setTimeout(
                        () => resolve(config),
                        waitPeriodForThisRequest);
                });
            }
        }

        lastInvocationTime = now;
        return config;
    }

    axiosInstance.interceptors.request.use(scheduler);
}

What it does is timing requests so they are performed at intervalMs milliseconds intervals.

In your code:

function getAlbums(artistID) {
    const deezerService = axios.create({ baseURL: 'https://api.deezer.com' });
    scheduleRequests(deezerService, 100);

    deezerService.get(`/artist/${artistID}/albums`)
        .then((albums) => {
            const urlRequests = albums.data.data.map(
                    (album) => deezerService
                        .get(`/album/${album.id}`)
                        .then(albumInfo => albumInfo.data));

            //you need to 'return' here, otherwise any error in album
            // requests will not propagate to the final 'catch':
            return axios.all(urls).then(console.log);
        })
        .catch(console.log);
}

This is, however, a simplistic approach, in your case you probably would like to receive the results as fast as possible for number of requests less than 50. For this, you have to add some kind of a counter inside the scheduler which will count the number of requests and delay their execution based both on the interval and the counter.

like image 15
galenus Avatar answered Nov 19 '22 05:11

galenus