0

I am trying to deal with 3 throttled API endpoints that I want to call each time I go through a loop. I'm trying to make sure there is a 500ms delay between each iteration of calls so I don't make too many requests, instead what happens is it makes all the calls after waiting 500ms * the number of iterations, which of course hits the request limit returning a 429.

What I've done is create what I thought would be a simple wait function, but it did not work so I set about to googling the problem. Almost every solution I've found has been the same type of function, which either ignores the wait, or does what I am experiencing now, or suggests using a 3rd party dependency which I am avoiding since this is the only place I do this in my code. Now I have a new wait function, slightly modified based on research. Here is my current code:

// wait function

async function wait(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}


// one of 3 identical api calls (except the endpoint changes, in this case I've filled it with 
example.com)

function callAPI(param) {
    return fetch (`https://example.com?param=${param}`)
        .then(res => {
            if (res.ok) {
                return res.json();
            }
        });
}


// Iteration

async function getEach(arr) {
    arr.forEach(async name => {
        await wait(500);
        await callAPI(name);
        // then api call 2, 3
        // then return, console log, append to obj or array, w.e. with the api results
    });
}

getEach(arrList);

What I'm hoping to find out is:

  1. Understanding why this is not behaving the way I think it should be
  2. How to achieve my desired results

Thanks

nghs
  • 129
  • 1
  • 15
  • 1
    Very similar to this question: https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop/37576787#37576787 – Wyck Aug 27 '20 at 14:58
  • @Wyck seems the solution is the same as is the cause of the problem, but the perceived issue is what differs – nghs Aug 27 '20 at 15:03

1 Answers1

2

Array.forEach will not wait for the wait() and callAPI() calls to complete, so they will be called very quickly in succession. But you can easily do this with either a for .. of loop or just a for loop.

For example:

async function getEach(arr) {
    for(let name of arr) {
        await wait(500);
        await callAPI(name);
    }
}

or

async function getEach(arr) {
    for(let i = 0; i < arr.length; i++) {
        await wait(500);
        await callAPI(arr[i]);
    }
}
Terry Lennox
  • 29,471
  • 5
  • 28
  • 40
  • 1
    Thank you! I've found a few examples using forEach for what ever reason, this explains why none of my attempts worked quite clearly. I'll accept this as soon as it lets me. – nghs Aug 27 '20 at 14:53
  • It's annoying that this is the behaviour from Array.forEach.. maybe not quite what we'd expect. But at least we can use the other looping constructs to get the job done! – Terry Lennox Aug 27 '20 at 14:56
  • 1
    Yes, that is annoying, I can't imagine if all the loops had the same behavior, that would drive me insane. I rarely use `forEach` but with a crisp array to iterate through with no mutations needed I didn't even think to try a `for` or `for of` instead. Thank you for saving the day, I've spent an embarrassing amount of time on this – nghs Aug 27 '20 at 15:01
  • 1
    I think we've all been confused by this one and lost time on it, at least we can benefit from each other's experience! – Terry Lennox Aug 27 '20 at 15:02