2

I feel like I may be doing something that Promises weren't designed to do, but here we go, this is what I'd like to do:

$http.get('/api/endpoint/PlanA.json').then(
    function success( response ) {
        if ( response.data.isAllGood ) {
            $scope.myData = response.data;
        }
        else {
            // TODO HERE! Call the failure function!
        }
    },
    function failure() {
        $http.get('/api/endpoint/PlanB.json').then(
            function planBsuccess( response ) {
                $scope.myData = response.data;
            }
        );
    }
).then(function doOtherStuff() {});

This works as expected if, for example the PlanA endpoint returns a 404 or 500... but if it succeeds, but has bad data (eg, isAllGood is false), then I'd like it to get to the failure callback. Is there an easy way to do this?

I've tried calling $q.defer().reject and returning that promise, however that just calls the failure callback for doOtherStuff.

Matt Grande
  • 11,964
  • 6
  • 62
  • 89

2 Answers2

1

Yes, there is an easy way to do it. You just have to chain the failure callback after the success callback, not next to it - see When is .then(success, fail) considered an antipattern for promises? for a more detailed explanation.

$http.get('/api/endpoint/PlanA.json').then(function success(response) {
    if (response.data.isAllGood) {
        $scope.myData = response.data;
    } else {
        return $q.reject(); // fail this promise
    }
}).catch(function failure() { // and handle it here (also errors originating from $http)
    return $http.get('/api/endpoint/PlanB.json').then(function planBsuccess(response) {
//  ^^^^^^ don't forget to wait with doOtherStuff for plan B
        $scope.myData = response.data;
    });
}).then(function doOtherStuff() {});

or even better, avoiding the code duplication of the $scope assignment:

$http.get('/api/endpoint/PlanA.json').then(function validate(response) {
    if (response.data.isAllGood)
        return response;
    else
        throw new Error("noGoodData");
}).catch(function failure() {
    return $http.get('/api/endpoint/PlanB.json');
}).then(function success(response) {
    $scope.myData = response.data;
    return doOtherStuff();
});
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

Don't do it. Its bad design. Here is a much simpler solution:

$http.get('/api/endpoint/PlanA.json').then(
    function success( response ) {
        if ( response.data.isAllGood ) {
            $scope.myData = response.data;
        }
        else {
             foo();
        }
    },
    function failure() {
        foo();
    }
).then(function doOtherStuff() {});

foo = function(){
  $http.get('/api/endpoint/PlanB.json').then(
        function planBsuccess( response ) {
            $scope.myData = response.data;
        }
   );
};

That being said, I think even this is abusing how $http was designed. I would instead do something like

$http.get('/api/endpoint/PlanA.json').then(
    function success( response ) {
      $scope.myData = response.data;
    },
    function failure() {
        trueFailureFunc();
    }
).then(validateData());

validateData = function(){
    if ( response.data.isAllGood ) {
      $scope.myData = response.data;
    }
     else {
        getDataFromOtherSource();
    }
}
David says Reinstate Monica
  • 19,209
  • 22
  • 79
  • 122
  • Thanks for this. I basically wrapped your second one in a function that returned a promise itself, and when it `resolved`, I called by `doOtherStuff`. Thanks! – Matt Grande Apr 28 '15 at 17:04