angularjspromiseangular-promiseangular-resource

AngularJS Promise retries


I am having some trouble getting a retry function to work and was hoping for some assistance. I have a $resource that I want to have called until a success condition occurs or the maximum retries has been exceeded.

The issue I seem to be running into is that within my retry function I am calling another promise and that is where the condition would be checked. I could get the code to function as intended by removing the added promise and creating a default success condition after a few retries but I cannot figure out how to correctly add the new promise call into the function.

resource is a stand-in for an Angular $resource which returns a $promise

My code is as follows:

resource.$promise.then(function (response) {
  return keepTrying(state, 5);
 }).then(function (response) {

 }).catch(function (err) {
   console.log(err);
 });

And the keepTrying function:

function keepTrying(state, maxRetries, deferred) {
  deferred = deferred || $q.defer();
  resource.$promise.then(function (response) {
    success = response;
  });
  if (success) {
    return deferred.resolve(success);
  } else if (maxRetries > 0) {
    setTimeout(function () {
      keepTrying(state, maxRetries - 1, deferred);
     }, 1000);
   } else if (maxRetries === 0) {
     deferred.reject('Maximum retries exceeded');
   }
   return deferred.promise;
 }

Solution

  • The problem with your attempt is that you are not re-querying the resource, but using the promise for an already-queried resource over and over.

    What you need to do is use a function that will (a) initiate the query, and (b) return the promise for that initiated query. Something like this:

    function () { return $resource.get().$promise; }
    

    Then you can pass it into something like this, that will do the retries.

    function retryAction(action, numTries) {
        return $q.when()
            .then(action)
            .catch(function (error) {
                if (numTries <= 0) {
                    throw error;
                }
                return retryAction(action, numTries - 1);
            });
    }
    

    Here's how you would start this off:

    retryAction(function () { return $resource.get().$promise; }, 5)
    .then(function (result) {
        // do something with result 
    });
    

    One nice thing about this approach is that even if the function that you pass to it throws an error upon invoking it, or doesn't return a promise at all, the retry functionality and the returning of the result via a resolved promise will still work.

    Example