Asma Rahim Ali Jafri
Asma Rahim Ali Jafri

Reputation: 1383

How to check the state of an angular-promise?

In my website, I have API's for twitter and facebook which enables the "mentions" feature (the one that pops up whenever we use the @ symbol)

However, it is often that the access token for some feature is often expires resulting in the API not working. I store all my API's in an array and then I need to check if the token has failed or not resulting in resolved or rejected API promise.

This is an older code that needs changing due $q.all. As $q.all works whenever all the promises have been resolved, thus triggering the .then() call, this results in the .then() function NEVER working in my case (as the Facebook API is never working)

I need to find a condition where each API is checked and the .then() runs for only that API that is resolved (twitter in this case) and ignores the failed API (Facebook in this case)


        if (selectedIds.allowed.TW) {
          usersApi.push(TS.loginResource.getTwitterProfiles({
            subUserId: selectedIds.allowed.TW,
            name: searchTerm
          }).$promise);
        }

        if (selectedIds.allowed.FB || selectedIds.allowed.FB_PAGE || 
            selectedIds.allowed.FB_GROUP) {
          $scope.post.showTags = true;
          usersApi.push(TS.loginResource.getFbPages({
            subUserId: selectedIds.allowed.FB_PAGE || selectedIds.allowed.FB 
            || selectedIds.allowed.FB_GROUP,
            name: searchTerm
          }).$promise);
        }


        if (usersApi.length) {
          $q.all(usersApi).then(function (responses) {
            var tags1 = responses[0];
            tags1.forEach(function (tag, i) {
              tags1[i].name = tag.name.replace(/\"/g, "");
            });
            $scope.post.tags = tags1;
            if (usersApi.length > 1) {
              var tags2 = responses[1]
              tags2.forEach(function (tag, i) {
                tags2[i].name = tag.name.replace(/\"/g, "");
              });
              $scope.post.tags = $scope.post.tags.concat(tags2);
            }
          })
        }
      }, 500);
    } else {
      $scope.post.tags = [];
      $scope.post.showTags = false;
    }

Upvotes: 0

Views: 789

Answers (2)

georgeawg
georgeawg

Reputation: 48968

$q.all is not resilient1

If one of the promises is rejected, the $q.all is rejected with the first error.

To create a resilient composite promise, that is a promise that waits for all the promises to complete pass or fail, use .catch on each individual promise to convert the rejected promise to a successful promise.

var resilientPromises = [];

angular.forEach(promises, function(p) {
    var resilientP = p.catch( function(result) {
        //return to convert rejection to success
        return result;
    });
    resilientPromises.push(resilientP);
});

$q.all(resilientPromises).then( function (results) {
    //process results
});

The two things to take away from this answer:

  1. A $q.all promise is not resilient. It is rejected with the first rejected promise.
  2. A fulfilled promise can be created from a rejected promise by returning a value to the onRejected function of either the .then method or the .catch method.

Upvotes: 1

elpddev
elpddev

Reputation: 4494

I think you are looking to chain backup response that catch the api error and return a new success resolved promise on each specific api call before you wait on "all" of them.

apiCalls.push(doTwiterStuff().then(handleTwiterSuccess, handleApiFailure);
apiClass.push(doFBStuff().then(handleFbSuccess, handleApiFailure);

Promise.all(apiCalls).then(arr => {
  arr.filter(x => !isNil(x)).forEach(x => doSomethingWithApiResult(x));
});

function handleApiFailure(x) {
  ...
  return Promise.resolve(null);
}

Hopes this helps.

Upvotes: 1

Related Questions