TGH TGH - 4 months ago 8
AngularJS Question

Executing then after catch

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?

Answer

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.


The .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.