Search code examples
javascriptsettimeout

Issues when trying to space XMLHttpRequest() API calls every 5 seconds with setTimeout()


I am working on the initial data pull for a project. It will be cached and further requests will be much less. I am attempting to space out my API requests every 5 seconds to avoid overloading the server and hitting the rate limit per their rules. It does not seem that my setTimeout() function is actually working as desired. I logged time stamps to see and all iterations seem to be happening at the exact same time. I think this somehow due to the asynchronous nature of javascript, but I'm not sure how to remedy it. Any help would be greatly appreciated.

Code Excerpt:

var leagues;
function getLeagues(){

    var reqURL = "url";
    var data = new XMLHttpRequest();
    data.open("GET", reqURL);
    data.responseType = "text";
    data.send();

    // parse data on load
    data.onload = function() {
        // League IDs
        leagues = JSON.parse(data.response)

        for (i = 0; i < 5; i++) {
            delay(i); // iterate every 5 secs
        }
    };
}

function delay(i) {
  setTimeout(() => {
    var d = new Date();
    console.log(d.getSeconds())
  }, 5000);
}

Solution

  • setTimeout is a browser API that will guarantees not an exact delay time to execute after but a minimum time to execute a function so it's not related to js.

    setTimeout cause the browser to set a timer after x milliseconds, when finished the function you pass will be in the event queue, but it won't necessarily execute, if event queue was completely empty, you are lucky and the function get executed immediately after 5 seconds, otherwise the function have to wait in queue until each function get executed before so we have (5 seconds + some times until the function get in the head of the queue then get executed).

    calling setTimeout five times in sequence (using a for loop) with the same time delay will add the same function 5 consequent times in the event queue, all will be executed at the same time because of the same time delay used for all of them. so a solution would be to use different delay time for each function.


    I'll suggest the solution for the same problem, because I faced it alot

    Callback based solution

    
    repeat(processingRequest, 5, 5000);
    
    
    function processingRequest(){
      console.log('Making request at', new Date().getTime());
    }
    
    
    function repeat(func, howManyTimes, milliSecondsInterval){
    
      if(howManyTimes > 0)
        delay(() => {
          func();
          howManyTimes--;
          repeat(func, howManyTimes, milliSecondsInterval);
        }, milliSecondsInterval);
    }
    
    function delay(func, milliSeconds){
      setTimeout(func, milliSeconds);
    }
    

    Promise based solution(more preferred)

    
    
    repeat(processingRequest, 5, 5000);
    
    
    function processingRequest(){
      console.log('Making request at', new Date().getTime());
    }
    
    function repeat(func, howManyTimes, milliSeconds){
      delay(milliSeconds)
      .then(() => {
    
        func();
        howManyTimes--;
    
        if(howManyTimes > 0)
          repeat(func, howManyTimes, milliSeconds);
      });
    }
    
    function delay(milliSeconds){
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve();
        }, milliSeconds);
      });
    }
    

    using a library like rxjs(providing a repeat and delay) will make things even more simple

    https://rxjs-dev.firebaseapp.com/api/operators/repeat

    https://rxjs-dev.firebaseapp.com/api/operators/delay

    even though not every application need rxjs, but we have to say it give the programmer a great power over clean sync or async operations


    [References]

    For further knowledge about Browser API & Event queue

    Cool talk from Philip Roberts

    https://youtu.be/8aGhZQkoFbQ?t=1165

    from THE BEST great js resource I've found

    https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/async%20%26%20performance/ch1.md