4

I'm using Axios to make API calls, and for one call I'd like to continue polling the API until I get a response.

However, when I call this function something resolves the promise earlier than expected.

I call the function here:

componentDidMount() {
  api.getUser(this.props.user.id)
  .then((response) => {
    console.log(response);
    this.handleSuccess(response.content);
  })
  .catch((error) => {
    this.handleError(error);
  });
}

And the console.log on line 4 shows undefined. The function does continue polling and stops when it receives valid data.

The function itself:

getUser(id, retries = 0) {
  return axios(getRequestConfig)
  .then((res) => {
    if (res.data && res.data.content.status === 200) {
      return Promise.resolve(res.data); // success!
    } else if (retries >= 15) {
      return Promise.reject(res); // failure
    } else {
      // try again after delay
      delay(1000)
      .then(() => {
        return this.getUser(id, retries + 1);
      })
    }
  })
  .catch(err => err);
}
Toby
  • 12,743
  • 8
  • 43
  • 75

2 Answers2

10

I'd outsorce the polling logic into a seperate function:

//expects fn() to throw if it failed
//if it runs out of retries, poll() will resolve to an rejected promise, containing the latest error
function poll(fn, retries = Infinity, timeoutBetweenAttempts = 1000){
    return Promise.resolve()
        .then( fn )
        .catch(function retry(err){
            if(retries-- > 0)
                return delay( timeoutBetweenAttempts )
                    .then( fn )
                    .catch( retry );
            throw err;
        });
}



getUser(id) {
    function validate(res){
        if(!res.data || res.data.content.status !== 200) 
            throw res; 
    }
    return poll(() => axios(getRequestConfig).then(validate), 15, 1000);
}
Thomas
  • 11,958
  • 1
  • 14
  • 23
  • I started with this logic, however the retry has to happen in the `.then` block, not the `.catch`block - the server returns a 400/500 for an error, and a different status code for continuous polling. – Toby May 18 '17 at 17:50
  • @Toby, that's what the `validate()` function is for in my code. Either it's a status 200 or consider it a fail. – Thomas May 18 '17 at 19:29
  • Ah - apologies, I missed that part of your answer. I'm going to revisit this method, many thanks again! – Toby May 18 '17 at 19:30
  • 1
    where defined function delay ? – Dmitry Malugin Oct 31 '19 at 12:20
  • 1
    @DmitryMalugin I believe the `delay` method is just a method wrapping `setTimeout` in a promise. I took this code straight from [mdn](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Creating_a_Promise_around_an_old_callback_API) and altered the name to delay `const delay = ms => new Promise(resolve => setTimeout(resolve, ms));` – rdrw Nov 15 '19 at 23:02
2

There is a library axios-request-handler that supports polling out of the box.

Mike Antoniadis
  • 657
  • 7
  • 12
  • The only downside of this library that I found was that it does not allow you to pass an instance of Axios in so any interceptors you have attached won't be used. Other than that it would have been exactly what I needed. – rdrw Nov 15 '19 at 23:04