I have the following fiddle: http://jsfiddle.net/thelgevold/3uv9nnjm/6/
angular.module('hello',[]).controller('helloController',function($q){
console.clear();
function someService(){
var deferred = $q.defer();
deferred.reject({e:'error'});
return deferred.promise;
}
function callService(){
return someService().then(function(obj){
console.log('first then');
}).
catch(function(e){
console.log('error1');
var deferred = $q.defer();
deferred.reject({e:'error'});
return deferred.promise;
});
}
callService().catch(function(e){
console.log('error2');
}).then(function(e){
console.log('second then');
});
});
It's essentially just a quick $q promise POC. My question is: Why does the last then clause get called when the promise is rejected? The output is as follows:
error1
error2
second then
I understand why error1/error2 are printed, but I thought the second then string should not be printed since the promise was rejected. I thought it would omit "second then" for the same reason the "first then" is omitted. Any thoughts?
If you return a normal value or a promise that eventually resolves from the . catch() handler (thus "handling" the error), then the promise chain switches to the resolved state and the . then() handler after the . catch() will be called.
The catch method is used for error handling in promise composition. Since it returns a Promise , it can be chained in the same way as its sister method, then() . catch() internally calls then() . This is observable if you wrap the methods.
In summary: then : when a promise is successful, you can then use the resolved data. catch : when a promise fails, you catch the error, and do something with the error information. finally : when a promise settles (fails or passes), you can finally do something.
The then callback gets called because the catch callback is before it, not after. The rejection has already been handled by catch . If you change the the order (i.e. ( promise.
Before I get started, don't do this:
var deferred = $q.defer();
deferred.reject({e:'error'});
return deferred.promise;
Do this:
return $q.reject({e:'error'});
Or preferably, this:
return $q.reject(new Error('error'));
Beware the deferred antipattern.
Now, for the answer to your question.
.catch()
after your call to callService()
is catching the error and not producing a new error. It has essentially "handled" the error, and the following .then()
handler is free to be called.
The synchronous code equivalent of your example would be:
function someService() {
throw { e: 'error' };
}
function callService() {
try {
var obj = someService();
console.log('first then');
} catch (e) {
console.log('error1');
throw { e: 'error' };
}
}
var e;
try {
e = callService();
} catch (e) {
console.log('error2');
}
console.log('second then');
I think that if you look at it this way, it makes perfect sense.
The relevant text in the Promises/A+ spec is here. For all intents and purposes, you can view the catch
handler as the same thing as an onRejected
handler:
2.2.7. then must return a promise [3.3].
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
Basically, your onRejected
handler is "returning" the value undefined
, so the promise produced by catch()
resolves with the value undefined
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With