1

I have an array with some values that should be passed as parameter to a api call. I want to update data with results from the api call. And I want to call the api every 1 second with each value of the array.

example :

const people = [1, 2, 3, 4, 5];

people.forEach((person, index) => {
  setTimeout(() => {
    fetch(`https://swapi.dev/api/people/${person}/`)
      .then(res => res.json())
      .then(data => {
        // do stuff, eg : update UI
        console.log(data.name);
      })
  }, index * 1000);

});

Since I want to keep calling the api and keep updating data or UI, is wrapping the forEach() in a infinite loop a good idea, like this :

 while(true) {
          people.forEach((person, index) => {
              setTimeout(() => {
                  fetch(`https://swapi.dev/api/people/${person}/`)
                    .then(res => res.json())
                    .then(data => {
                        // do stuff, eg : update UI
                        console.log(data.name) ;
                      })
                    }, index * 1000);

              }) ;
            } ;

if it's a bad idea please mention why and suggest other methods to do it right. Thanks in advance.

Bit more info :- The api I want to use has a rate limit of 100 api calls per 5 seconds, So I will have to call it every 3 seconds to keep it real time without hitting the limit.

Each array element is different endponts so it needs to be iterated completely to keep the data as real time as possible.

this script is supposed to run 24/7 to update the data

the starwars api is just used as an example because that was the first one that came into my mind.

me.nkr
  • 108
  • 1
  • 7
  • 2
    That's a really bad idea because the while loop iteration will not wait for the timeouts. You'll be scheduling thousands of timeouts per second that way. You can either use an outer interval so the loop runs every 5 seconds (quick but bad fix), or use staggered intervals. –  May 07 '21 at 14:22
  • can you please explain ? – me.nkr May 07 '21 at 14:23
  • I would suggest doing a `setInterval()` at the bare minimum instead of a while. Can your api be changed to accept any number of people instead of just one person at a time? how often does their data change? – rhavelka May 07 '21 at 14:23
  • Use your first snippet but call fetch() inside a `setInterval(..., people.length * 1000)` callback. That way the 1st fetch request will run after 1, 6, 11 seconds, the next after 2, 7, 12 seconds, etc. –  May 07 '21 at 14:24
  • @ChrisG Pardon me but can you please elaborate a bit more ? Also this is an script that's gonna run forever maybe. – me.nkr May 07 '21 at 14:39
  • I'm not sure what there is to elaborate tbh, but just in case you missed it, I'm using [**setInterval**](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval) –  May 07 '21 at 14:41
  • I mean a code example, I'm still not good with the setInterval/setTimeOut functions. – me.nkr May 07 '21 at 14:43
  • 1
    I meant like this: https://jsfiddle.net/zj19g6pk/ –  May 07 '21 at 14:47
  • try the code `while(true) { setTimeout(() => console.log('hello world'), 5000); }` you will see that it is fine for 5 seconds, and then it blows up. that is because `setTimeout()` runs async to the main thread, you might be thinking of [await sleep](https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep) but setInterval is a better solution for what you want – rhavelka May 07 '21 at 14:49
  • So that worked except there's a {{array.length}} seconds delay before starting the api call. Is there a workaround ? – me.nkr May 07 '21 at 14:55
  • 1
    Did you look at my fiddle? I moved the call to a function and I'm calling it in the initial loop specifically to prevent that delay. –  May 07 '21 at 14:56
  • commented before looking your fiddle. That one is perfect for me. Will take sometime to understand how that works, but Thanks for the answer – me.nkr May 07 '21 at 15:05
  • That api lets you request all the people at once (in one request) - via /people/ - that would be a lot more efficient than sending a separate request for each person. – James May 07 '21 at 16:24
  • @James Thanks for the info but I was using that as an example. – me.nkr May 07 '21 at 16:57
  • @ChrisG Your solution works like a charm. It would be great if you can post your fiddle as answer so I can mark it as most apt one. Also I was trying to figure out how that solution worked, what I understood is that the forEach loop exexuted and exited but while executing it triggered the setTimeouts, and when the setTimeouts ran it triggered the setIntervals. and all of those actually ran outside forEach loop but was able to access the variables because of closure. Am I right ? – me.nkr May 07 '21 at 17:02
  • Exactly right, yes. –  May 07 '21 at 17:23

2 Answers2

1

Something that you can do to get the desired behavior you want is using a setInterval() with an interval time of your people array's length.

const people = [1, 2, 3, 4, 5];

setInterval(() => {
  people.forEach((person, index) => {
    setTimeout(() => {
      fetch(`https://swapi.dev/api/people/${person}/`)
        .then(res => res.json())
        .then(data => {
          // do stuff, eg : update UI
          console.log(data.name);
        })
     }, index * 1000);
  });
}, people.length * 1000);

But right now you are being extremely chatty with your server and I would suggest doing a batch get, where you get all of the users at once instead of individually (and that you can call more often to keep the ui as up to date as possible). I assume this is your api and you can add a new function if you so choose.

const people = [1, 2, 3, 4, 5];

setInterval(() => {
  people.forEach((person, index) => {
    fetch(`https://swapi.dev/api/people/getList/`, {
         method: 'GET',
         body: JSON.stringify(people)
      })
      .then(res => res.json())
      .then(data => {
        // do stuff, eg : update UI
        console.log(data.name);
      })
  });
}, 3000);
rhavelka
  • 2,283
  • 3
  • 22
  • 36
  • the data that api returns is also getting updated in realtime, so I think I'll have to keep calling to get the updated data – me.nkr May 07 '21 at 14:37
  • @me.nkr That is what `setInterval` does here, as opposed to `setTimeout` –  May 07 '21 at 14:41
  • @me.nkr Chris is right, the function `setInterval()` is basically an infinite loop that you can set a delay on for each iteration – rhavelka May 07 '21 at 14:42
  • so what should be the interval ? and is it better than a infinte loop using while(true) ? – me.nkr May 07 '21 at 14:46
  • @me.nkr the interval would be `people.length * 1000`. so if you have 10 people it will wait 10 seconds before calling the first person again. if you have 5 people it will wait 5 seconds before calling that first person again. and it is a lot better because `while(true)` runs as fast as it can. it doesn't wait for the settimeout to do its thing – rhavelka May 07 '21 at 14:55
1

This was originally provided by @Chris G

This one is working like a charm

const people = [1, 2, 3, 4, 5];

function apiCall(person) {
      fetch(`https://swapi.dev/api/people/${person}/`)
        .then(res => res.json())
        .then(data => {
          // do stuff, eg : update UI
          console.log(data.name);
        });
        
}

people.forEach((person, index) => {

  setTimeout(() => {
        apiCall(person);
    
    setInterval(() => {
            apiCall(person);
    }, people.length * 1000);
    
  }, index * 1000);

});

Just because I took sometime to understand how the above code works, I'm gonna provide some detail below :

The forEach loop executed and exited but while executing it triggered the setTimeouts, and when the setTimeouts ran it triggered the setIntervals. And all of those actually ran outside forEach loop but was able to access the variables because of closure

me.nkr
  • 108
  • 1
  • 7