4

EDIT: SEE THE ANSWER BELOW

I have a function:

dataFactory.getCurrentStepId(wf.identifier, ctx.identifier)
   .then(function (data) {
      console.log('then');
      $timeout(function () {
      $("#app-node-" + data.identifier)
           .parent().parent().css("border", "5px solid green");
      });
    })
    .catch(function () {
      console.log('error');
      alert('Error!')
      });

The getCurrentStepId has an exception, and I see the error printed on the console for the AJAX request it sends, but my alert and console.log don't fire in the catch.

Any ideas why? Here is the stuff it's calling:

app.factory('dataFactory', function ($http, $q) {
    var _baseUrl = webServiceContext;
    var _doGet = function (endpoint) {
        var deferred = $q.defer();
        $http.get(_baseUrl + '/' + endpoint).then(function (response) {
            deferred.resolve(response.data);
        });
        return deferred.promise;
    };

    var getCurrentStepId = function (wid, cid) {
        return _doGet('wf/step/' + wid + '/' + cid);
    };
})

Here is the answer

@Blunderfest almost got it right:

 $http.get(_baseUrl + '/' + endpoint).then(function (response) {
            deferred.resolve(response.data);
        }).catch(function(e){return deferred.reject(e);});
mikeb
  • 10,578
  • 7
  • 62
  • 120

3 Answers3

2

Make sure you return a rejected promise in your catch blocks. So this:

$http.get(_baseUrl + '/' + endpoint).then(function (response) {
        deferred.resolve(response.data);
    })

Should be this:

$http.get(_baseUrl + '/' + endpoint).then(function (response) {
        deferred.resolve(response.data);
    }).catch(function(e){
         return $q.reject(e);
    });

Actually, you can use $q.reject as a shorthand for rejecting a promise, so I think it's a correct answer :). You can also use return $q.when(response) as a shorthand for deferred, resolve and all that.

I recommend reading: $q.defer: You're doing it wrong

Really, I would re-organize your code to follow a few best practices on promises, especially in angular. It's better to return the $http request itself (which is a promise):

app.factory('dataFactory', function ($http, $q) {
    var _baseUrl = webServiceContext;
    var _doGet = function (endpoint) {
        // Returns the promise wholesale.
        return $http.get(_baseUrl + '/' + endpoint).then(function (response) {
            return response.data;
        }).catch(function(e){
            // ensures the returned promise is rejected up the chain.
            return $q.reject(e);
        });
    };

    var getCurrentStepId = function (wid, cid) {
        return _doGet('wf/step/' + wid + '/' + cid);
    };
})

Angular Docs for $q methods:

reject(reason) – rejects the derived promise with the reason. This is equivalent to resolving it with a rejection constructed via $q.reject

The reason you need to do this is that if you do not catch errors and reject promises at lower levels, the promise chain up the line will not know the promise is rejected and treat it as a success.

Blunderfest
  • 1,854
  • 1
  • 28
  • 46
  • If you're still manually creating the deferred object, that may be the case, I'm not sure. To return a rejected promise, it definitely works. In what way does it not work? Does it not return a rejected promise or does it just error out? – Blunderfest Jan 29 '16 at 18:41
  • As another example: http://stackoverflow.com/questions/24443733/angulars-q-reject-vs-deferred-reject – Blunderfest Jan 29 '16 at 18:44
0

I find this article has great explaination and example

  1. Inside the promise, the catch() method will catch the error caused by the throw statement and reject().

  2. If an error occurs and you don’t have the catch() method, the JavaScript engine issues a runtime error and stops the program.

yu yang Jian
  • 6,680
  • 7
  • 55
  • 80
-1

try this instead

dataFactory.getCurrentStepId(wf.identifier, ctx.identifier)
   .then(function (data) {
      console.log('then');
      $timeout(function () {
      $("#app-node-" + data.identifier)
            .parent().parent().css("border", "5px solid green");
  }, function (err) {
     console.log(err);
     alert(err);
  });

remember then takes 2 parameters - a function if successful, a function if error occurred. You only provided success.

Antiokus
  • 534
  • 3
  • 8
  • 2
    The way I'm using is equivalent: From the docs `The catch() method returns a Promise and deals with rejected cases only. It behaves the same as calling Promise.prototype.then(undefined, onRejected).` -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch – mikeb Jan 29 '16 at 18:19