$q promise error callback chains
Solution 1
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"); });
});
Solution 2
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();
Solution 3
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.
Related videos on Youtube
![Steven Wexler](https://i.stack.imgur.com/j2t3q.jpg?s=256&g=1)
Steven Wexler
Freelance Software Developer Formerly a Software Developer at Hurdlr Formerly a Lead engineer at Applied Predictive Technologies Author of exceptions.js Contributor to Code Ducky
Updated on November 22, 2020Comments
-
Steven Wexler over 3 years
In the following code snippet
error 1
andsuccess 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>
-
aarosil over 9 yearsuse
.catch()
instead of.then(onSuccess, onFailure)
-
Steven Wexler over 9 yearsHow would that help? That's just a shorthand method for then. If you look at Angular's source code you'll find
"catch": function(callback) { return this.then(null, callback); }
. -
aarosil over 9 yearsWell, if you only have success handler in
then
, it would propagate down to thecatch
. You implicitly reject without using$q.reject
simply by not handling it -
Steven Wexler over 9 yearsOk, what you're telling me is very useful!
then(angular.noop, angular.noop)
is different than callingthen(angular.noop, null)
because angular will assume you did not try to correct the error if you don't pass in an error callback, so it will propagate the rejection by presumably calling$q.reject
(or something analgous) under the hood. However, it will assume you tried to correct the error if you provide an error callback. To not confuse other readers, we should point out this has nothing to do with.catch
. -
aarosil over 9 yearsThey are no different - both are handlers.
catch
is only shortcut for.then(null, fn)
. I find more readable too -- to leavethen
only for successes and placecatch
as needed to get as finegrained control as you want, doing things like instanceOf on the errors and handling or rethrowing etc as you need to (reject
insidethen
appears odd to me). -
aarosil over 9 yearscheck ths out: stackoverflow.com/a/24663315/2845029
-
Steven Wexler over 9 yearsThey are different. Try it for yourself. You'll find later success callbacks are invoked with the first example and later error callbacks are invoked with the second example. The simpliest code to try is:
.then(angular.noop, angular.noop).catch(function () { console.log("caught"); }
vs.then(angular.noop, null).catch(function () { console.log("caught"); }
. You'll find the log statement is never printed in the first code snippet. Note, the second code snippet is equivalent to.then(angular.noop).catch(function () { console.log("caught"); })
. -
Steven Wexler over 9 yearsOn a related note,
.then(function () { /*do stuff*/}).catch(function () { console.log("caught"); })
is equivalent to.then(function () { /*do stuff*/}).then(null, function () { /*handle errors*/ })
and arguably more useful than.then(function {/*do stuff*/}, function () { /*handle errors*/})
because you can handle the errors from your success callback.
-
-
AgmLauncher almost 8 yearsThat part of the documentation is VERY easy to miss, since the "Chaining Promises" section that comes before it, makes absolutely no mention of the fact that promise errors do not automatically propagate.
-
wholladay over 5 yearsThanks for explaining WHY the error handler needs to return $q.reject() in order to continue propagating the error.