Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$q promise error callback chains

Tags:

In the following code snippet error 1 and success 2 will be logged. How can I can I propagate error callbacks being invoked rather than the success callbacks being invoked if the original deferred is rejected.

angular.module("Foo", []);
angular
.module("Foo")
.controller("Bar", function ($q) {
    var deferred = $q.defer();
      deferred.reject();

      deferred.promise
          .then(
              /*success*/function () { console.log("success 1"); },
              /*error*/function () { console.log("error 1"); })
          .then(
              /*success*/function () { console.log("success 2"); },
              /*error*/function () { console.log("error 2"); });
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="Foo">
    <div ng-controller="Bar"></div>
</div>
like image 689
Steven Wexler Avatar asked Oct 18 '14 16:10

Steven Wexler


3 Answers

Error is propagate by returning $q.reject in the error callback

    var deferred = $q.defer();
      deferred.reject();

      deferred.promise
          .then(
              /*success*/function () { console.log("success 1"); },
              /*error*/function () { console.log("error 1"); return $q.reject('error 1')})
          .then(
              /*success*/function () { console.log("success 2"); },
              /*error*/function () { console.log("error 2"); });
});
like image 116
Chandermani Avatar answered Sep 24 '22 18:09

Chandermani


think of success/failure as try/catch

try{
    var val = dummyPromise();
} catch (e){
    val = "SomeValue";
}

if catch does not throws an exception, it is considered that the error is handled and hence outer calling function does not sees the error which occured in inner function.

Similar stuff happening here, you have to return return $q.reject(); from a promise in order for the next promise in the chain to fail too. See example plunker: http://plnkr.co/edit/porOG8qVg2GkeddzVHu3?p=preview

The reason is: Your error handler may take action to correct the error. In your error-function your dealing with the error,if not specified otherwise, it will return a new promise which is resolved. Therefore it is not reasonable to have the next promise failing by default (try-catch analogy).

By the way, you can return $q.reject() even from a success handler, if you sense an error condition, to have the next promise in the chain failing. You're catching the error and handling it - so it gets to the success handler. If you want to reject it, you have to do it by returning $q.reject();

like image 34
harishr Avatar answered Sep 23 '22 18:09

harishr


To sum the comments up, to propagate errors in the promise chain, either:

1) Do not provide an errorCallback for then:

deferred.promise
.then(
  /*success*/function () { console.log("success 1"); },
.then(
  /*success*/function () { console.log("success 2"); },
  /*error*/function () { console.log("error 2"); }); // gets called

Or

2) Return $q.reject() from the errorCallback:

deferred.promise
.then(
  /*success*/function () { console.log("success 1"); },
  /*error*/function (err) { console.log("error 1"); return $q.reject(err); });
.then(
  /*success*/function () { console.log("success 2"); },
  /*error*/function () { console.log("error 2"); }); // gets called

From the angular $q.reject documentation:

This api should be used to forward rejection in a chain of promises.
like image 7
user1338062 Avatar answered Sep 22 '22 18:09

user1338062