2

I'm having an issue with an $http Promise not being bubbled up from the service where it is being executed to any place where the service method is called.

I have an .authozire() method in the service that returns the $http Promise, as follows:

// auth.service.js

function authorize(authParams) {
  var request = {
    method: 'POST',
    url: _apiUrl + 'oauth/token',
    data: authParams
  };

  return $http(request)
    .then(
      function successHandler(response) {
        // Correctly executed when success
        $log.debug('authService :: authorize => success', response);
      },
      function errorHandler(response) {
        // Correctly executed when error
        $log.debug('authService :: authorize => error', response);
      }
    );
}

The success and error methods above (please not that I'm already not using the .success() and .error() methods as they are deprecated) work just fine in each case, but it doesn't work as expected when I call this method from a controller, like this:

// signin.controller.js

function submit() {
  authService.authorize(vm.formData)
    .then(
      function successHandler(response) {
        // Always executed even when there's an error
        $log.debug('SignInController :: submit :: authService.authorize() => success', response);
      },
      function errorHandler(response) {
        // NEVER executed
        $log.debug('SignInController :: submit :: authService.authorize() => error', response);
      }
    );
}

The first method successHandler is always called, even when there's an error and the service executed its own errorHandler.

It seems like the Promise being returned by the service it's just a simple Promise that accepts .then(), but doesn't differentiate success and fail like explained in the docs:

Returns a Promise that will be resolved to a response object when the request succeeds or fails.

So... Am I missing something? Does anyone had a problem like this before?

Thanks!

2
  • Are you definitely attaching it to the object returned in the service? i.e. var service = {}; service.submit = submit; Commented Mar 10, 2016 at 10:27
  • @Katana24: Yes, everything is attached as necessary and being executed just fine, except for when there's an error. In the service: service = {authorize: authorise}; return service; and in the controller: vm.submit = submit; Commented Mar 10, 2016 at 10:30

3 Answers 3

4

I believe you have a similar feeling I had when I was getting to know promises. Here is an answer to a question I posed that was similar to yours. I was just suprised that the error wasn't propogating. Using Kris Kowal's Q. How should I catch if any errors have been thrown throughout the life of a chained promise?

To quote that answer:

A handled rejection is like a caught exception. It stops propagating since well, it was handled. If you want to handle the rejection and keep it rejected you need to rethrow, again, just like in synchronous code.

try {
   throw new Error();
} catch(e){
    // handle error   
}
// no error here this code will keep running.

If you want it to keep rejecting and handle it, you need to rethrow:

try {
   throw new Error();
} catch(e){
    // handle error   
    throw e;
}
// this code will not run

The same with promises, just like you wrote. This is not particularly odd about promises, this is how synchronous exceptions work as well. If you want to propagate - you re-throw, otherwise - the errors are considered handled.

  1. So you can continue logging the error in your service and throw the same error you received for the next part in the chain by adding return $q.reject(response); in your service. You could also just say throw response; at the end of your service as well without the return and it will carry the error to the next part of the chain.
  2. You can decide not to log the error in your service and handle it straight in your controller.

I'd personally go with 1, but that's because I can't stand when promises aren't logging their errors it's just hard to track down what's breaking without the logging.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for the explanation. I think that I was so used to how it worked with .success() and .erro() that I forgot how promises work 😜 Also, it is necessary to return response.data on the successHandler if you need it on the controller.
Yes. They are very similar in some aspects to their synchronous counter-parts try catch, it's very hard to know how everything is supposed to work when learning new concepts. Yeah, my intention was that you could throw response; in the catching part of your service if you get what I'm saying
Funny thing is that this is not a new concept for me, I just simply ignored them since I was expecting something else to happen. But it was good to open the mind a bit. 😉
4

In auth.service.js errorHandler must return rejected promise:

return $q.reject(response);

2 Comments

Your solution worked, but before I accept it, would you mind explaining why? Did something change? Because it used to work like this before. Angular just automatically passed the Promise the way it was and it could be treated up in the chain.
errorHandler as successHandler returns just promise which can resolved successfuly or rejected. See @John answer for details, and you can accept his answer.
0

The problem is that you are already dealing with callbacks of promise in your authentication service.

To resolve this issue we have two possible solutions:

Inside authorize method create a promise and return it.

function authorize(params) {
   // create a promise object
   var dfd = $q.defer();

   var request = []; // request params here

   $http(request).then(
   function SuccessHandler(res) {
      def.resolve(res);
   }, 
   function ErrorHandler(res) {
      dfd.reject(res);
   });

   return dfd.promise();
}

Another way is to send the http object without treating callbacks within the method authorize

return $http(request);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.