0

I have an API to call every 2.5 seconds. Initially, the data inside the response object is null as the database is still updating it through a transaction. But on the subsequent 3rd or 4th try, I get the data. I am writing a reusable function for the same, however I get undefined. My goal is to keep calling the API until I get the value in my path and close the connection. Please advice.

P.S: The below API URL doesnt have any delay, but my private API has.

const getData = (url, path) => {
  const interval = setInterval(async () => {
    const result = await axios.get(url);
    if (_.has(result.data, path) && result.data[path]) {
      return result[path]
    }
  }, 2500)
  return clearInterval(interval)
}

getData('https://api.oceandrivers.com/static/resources.json', 'swaggerVersion')
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

JS Fiddle URL

Please advice.

arunmmanoharan
  • 2,535
  • 2
  • 29
  • 60

3 Answers3

5

You

return clearInterval(interval); // undefined

If you want to return a Promise which will resolve when the data is available, you could do something like this:

const getData = (url, path) => {
  return new Promise((resolve, reject) => {
    const interval = setInterval(async() => {
      const result = await axios.get(url);
      if (_.has(result.data, path) && result.data[path]) {
        clearInterval(interval); // Clear the interval
        resolve(result.data[path]); // Resolve with the data
      }
    }, 2500);
  });
}

getData('https://api.oceandrivers.com/static/resources.json', 'swaggerVersion')
  .then(data => {
    console.log(data); // Your data is available here
  });

// OR

(async () => {
  const version = await getData('https://api.oceandrivers.com/static/resources.json', 'swaggerVersion');
  console.log(version);
})();
blex
  • 24,941
  • 5
  • 39
  • 72
  • I still get undefined: https://jsfiddle.net/ftjgazbe/ – arunmmanoharan Jun 22 '20 at 18:34
  • Ah, yes, it should be `resolve(result.data[path]);` https://jsfiddle.net/kcw1vsjq/ – blex Jun 22 '20 at 18:36
  • Thanks man. Is there a way to return the data directlty from the function itself rather than me doing .then on getData ? – arunmmanoharan Jun 22 '20 at 18:38
  • @a2441918 No, since it's asynchronous. But you could make it look like it by using `const version = await getData(...);` (must be inside an `async` function, though) – blex Jun 22 '20 at 18:39
  • I see that the API call is made only after 2.5 seconds. Is there some way it can be immediate at first and then 2.5 seconds from the second until the promise is resolved? – arunmmanoharan Jul 02 '20 at 18:35
  • 1
    @a2441918 Sure, you can create a loop with an [IIFE](https://stackoverflow.com/a/8228308/1913729) and `setTimeout`: https://jsfiddle.net/2k8z5wyn/ – blex Jul 02 '20 at 18:40
  • One last question: I have the call running in a modal. While the call is running and lets say its not able to find the data at the path, and I close the modal, the API call is still being made. Could you enlighten me on a place wherein I call the clearTimeout? – arunmmanoharan Jul 02 '20 at 20:16
  • 1
    To be able to use `clearTimeout` from somewhere else (the function where you close the modal), the timeout needs to be available outside of the `getData` function. Here is an example _(don't look at what I did to axios and `console.log`, this is just for the demo)_: https://jsfiddle.net/a3o5myhb/ – blex Jul 03 '20 at 06:15
  • Thanks man. That worked like a charm. I was reading about this yesterday and came across https://stackoverflow.com/questions/49906437/how-to-cancel-a-fetch-on-componentwillunmount. Will something like https://stackoverflow.com/a/52225987/6731368 and https://medium.com/@rajeshnaroth/writing-a-react-hook-to-cancel-promises-when-a-component-unmounts-526efabf251f work as well? Thanks again buddy. – arunmmanoharan Jul 03 '20 at 15:01
  • https://stackoverflow.com/questions/65694196/poll-api-until-a-path-in-the-response-object-is-successful-failure-typescrip ... Can you help me on this please? – arunmmanoharan Jan 13 '21 at 03:15
1

Its because javascript is asynchronous as above comment is already mentioned. You can use either callbacks or promise in javascript. Here is the code:

const getData = (url, path, cb) => {
  const interval = setInterval(async () => {
    const result = await axios.get(url);
    if (_.has(result.data, path) && result.data[path]) {
     clearInterval(interval); //We found it remove the interval
      cb(result.data[path]);
    }
  }, 2500);
};

getData(
  "https://api.oceandrivers.com/static/resources.json",
  "swaggerVersion",
  data => {
    console.log("test",data);
  }
);

Here is the fiddle: https://jsfiddle.net/7pc4hq6t/3/

Shubham Verma
  • 4,918
  • 1
  • 9
  • 22
  • Thanks man. Is there a way to return the data directlty from the function itself rather than me doing .then on getData ? – arunmmanoharan Jun 22 '20 at 18:39
  • No, since javascript run this on a different thread. When this result has come it need some type of communication to know the code it's done. So for that, you can use either promises or callbacks. – Shubham Verma Jun 22 '20 at 18:44
  • Is there a way to trigger the API call immediately and then for the second try wait 2.5 seconds? – arunmmanoharan Jul 02 '20 at 18:37
  • See you can do that create fetch function for API. As dom load try hit that API. And in setinterval check if data is not available again hit the API. I would suggest instead of handling all case by yourself try asyncjs[https://caolan.github.io/async/v3/]. It will definitely help you alot. – Shubham Verma Jul 03 '20 at 05:16
1

You could create an asynchronous delay:

const delay = milliseconds => new Promise(resolve, setTimeout(resolve, milliseconds));

Then use like this:

const getDataAsync = async (url, path) => {
    while (true) {
        const result = await axios.get(url);
        if (_.has(result.data, path) && result.data[path]) {
            return result.data[path];
        }
        await delay(2500);
    }
}

const data = await getDataAsync('https://api.oceandrivers.com/static/resources.json', 'swaggerVersion');

This avoids the multiple layers of nested callbacks, and produces much more readable code.

Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35
  • 1
    @a2441918 An `async` function implicitly returns a `Promise` when the first `await` is hit. That `Promise` is resolved when you return something (or rejected when an error is thrown). `async` / `await` is essentially syntactic sugar over a `Promise`-returning method. – Johnathan Barclay Jun 23 '20 at 07:29