1

I apologize if this isn't a good question, but it's something that's confusing me a bit.

I'm attempting to return specific data from an $http.post() from within a factory, however it would appear that $http always return the original promise. I'm looking to avoid .success and .error given their possible depreciation in v1.5. Given that the factory might do other things such as set items in localStorage etc, I do not want to return $http.post() directly.

Anyways, is the following the best way to return specific data from an angular $http promise?

function login (email, password) {
  var deferred = $q.defer();

  $http.post('/api/auth', {
    email: email,
    password: password
  })
    .then(function (data) {
      return deferred.resolve('success');
    })
    .catch(function (data) {
      return deferred.reject('fail');
    });

  return deferred.promise;
}
Patrick Wolf
  • 1,329
  • 11
  • 28

2 Answers2

1

You don't need to create a deferred object. Instead, you can just return the result from $http.post. $http.post returns a promise that happens to have an extra two methods (success and failure).

function login(email, password) {
  return $http.post('/api/auth', {
    email: email,
    password: password
  })
  .then(function (data) {
    var newData = translateData(data);
    //now the data will be passed to the next promise
    return newData;
  })
  .catch(function (reason) {
    /*do stuff with failure*/
    //Now the rejection reason will be propagated to the next promise
    return $q.reject(reason);
  });
}

login()
  //You should get your data here.
  .then(function (data) { console.log(data); }) 
  .catch(function (reason) { console.log(reason); });

You may be interested to read this blog post which explains how to propagate data and rejection reasons through a promise chain.

Steven Wexler
  • 16,589
  • 8
  • 53
  • 80
  • I understand that bit, but I want to handle things within a service to abstract the http logic away from the controller. Sorry I should have been more clear. – Patrick Wolf Mar 13 '15 at 03:45
0

I would have written it with the error response as 2nd callback of the 'then' method (my example below). This way the error callback will only get called if there is an error with that $http request.

function login (email, password) { 
   var deferred = $q.defer();
   $http.post('/api/auth', {
     email: email,
     password: password
   })
   .then(function (data) {
     return deferred.resolve(data);
   }, function (message) {
     return deferred.reject(message);
   });
   return deferred.promise;
}

The way you have done it - using catch() - means it will get called if anything goes wrong in a promise chain. Hence, catch() would most likely be used at the end of several promises. For example, Something like this

CustomerService.login(email, password)
   .then(getUserData)
   .then(setUpAccount)
   .catch($log.error);

See this great post, which explains it far better than I did

Also, check out the docs on promises, the section on 'The Promise API'

Mark Collins
  • 378
  • 1
  • 2
  • 8
  • 1
    It's still the [deferred antipattern](http://stackoverflow.com/q/23803743/1048572). – Bergi Mar 13 '15 at 04:00
  • Bergi, I agree with you and thanks for pointing that out. I understand more now. All that aside, couldn't you just catch the specific error? IE catch(SpecificError, function (e) {}). IE chain specific catch handlers that focus on what you might want to handle then have a catch all. – Patrick Wolf Mar 13 '15 at 04:34
  • Ok, I might be blending the similarities between bluebird and q. It doesn't look like q can have a predicate in the catch chain like I specified. IE catch TypeError – Patrick Wolf Mar 13 '15 at 05:01