Home > Software design >  how to run map function (async, fetch, promise) every second one after another and not parallel?
how to run map function (async, fetch, promise) every second one after another and not parallel?

Time:05-06

I have a map function here, which is supposed to fetch data from an online source. The Problem is, that the API does only allow to fetch once per second. so I tried to do that like this:

const sleep = (ms: number) => {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
};

const promises = projectData.map(async (project, index) => {
        return await getAddress(asdf)
          .then((nominatimData) => {
        // ....do something...
          })
          .catch(async (e) => {
            console.log("failed", e);
            return await sleep(2000).then(() => {
              console.log("another try... ")
              return getAddress(asdf);
            });
          });
    });

    await Promise.allSettled(promises);

    console.log("Project Data", projectData); //does still fire before all getAddress() are present.. :(

there are two problems I cannot figure out:

  1. map() seems to do its job parallel (is that true?) which is kind of not what I want here - because like I said I need it to work preferably one by one every second

  2. my sleep function does not seem to work at all. the console log is just a mess and everything at once

what would be the correct way to make this work once a second? and is the fallback (just running "getAddress) again ok like that?

thank you so much!

CodePudding user response:

To answer your question:

  1. Yes, map does not wait for the Promise to finish to proceed to the next operation, but you can fix this using a for loop.
  2. Because you run the operations with map, your sleep function runs for every item in the array at once in parallel, the logic is good, not the method

You could use the for loop that allows async/await operations, although, since your goal is to wait for a response from the server I would recommend a custom cursor loop instead

let cursor = 0;
const timeout = 1000;
const sleep = (ms) => {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
};

const loop = async () => {
    // Break if index is out of bounds
    if (!projectData[cursor]) {
        return console.log('Done');
    }

    await getAddress(projectData[cursor]).then((nominatimData) => {
        // ....do something...
        // Proceed to the next element in the array
        cursor  ;
        await loop();

    }).catch(async (e) => {
        console.log(`Failed, retrying in ${ timeout }ms`);
        await sleep(timeout);
        await loop(); // Retry the loop with the same cursor index
    });
};
await loop();

CodePudding user response:

setTimeout() is a function that works ONCE, delayed by the time you specified. On the other hand, setInterval() is a function tha executes whatever it is inside, on an infinite loop, every second (or every time you specified).

You can check the docs here: setTimeout() and setInterval()

What I would do is to wrap the function that fetches from the API inside of a setInterval(), something like this:

setInterval(() => {    
  your code here
}, 1000);

And you can change those 1000 (1000ms or 1s) for any time you want.

  • Related