Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

chaining ngResource $promise success and errors

I'd like to know if it's possible to handle the $promise returned by ngResource on multiple levels so that the code is DRY

here is a simple example

aService = function(aResource) {
  var error, success;
  success = function(response) {
    console.log('Service Success');
  };
  error = function(response) {
    console.log('Service Error');
  };

  this.servicePostReq = function() {
    return aResource.save().$promise.then(success, error);
  };
  return this;

angular.module('app.service').factory('aService', ['aResource', aService]);

this works fine so far... it Service Success when response is OK and it Service Error when response is not OK

but when I add a controller that use this aService like following

aController = function(aService) {
  var error, success;
  success = function(response) {
    console.log('Controller Success');
  };
  error = function(response) {
    console.log('Controller Error');
  };
  this.controllerPostReq = function() {
    aService.servicePostReq().then(success, error);
  };

  return this;
};

angular.module('app.controller').controller('aController', ['aService', aController]);

the controller always success...

so if the request return success the output is

Service Success
Controller Success

and if the request fails the output is

Service Error
Controller Success

how do I chain the promise so that I don't have to add the code handled in the service for every controller that use the service ?


2 Answers

The problem is your service. Change this:

  this.servicePostReq = function() {
    return aResource.save().$promise.then(success, error);
  };

To this:

  this.servicePostReq = function() {
    return aResource.save().$promise.then(success);
  };

Explanation:

Since your service returns aResource.save().$promise.then(success, error), it's returning a new promise with an error handler included. Later, in your controller, you add onto the chain like this.

aService.servicePostReq().then(success, error);

The complete promise chain at this point looks if you expand it out:

return aResource.save().$promise
  .then(successFnFromService, errorFnFromService)
  .then(successFnFromController, errorFnFromController);

Since you catch the error from aResource.save() with errorFnFromService, the promise chain is basically "clean" at this point and it will just continue with the next then.

By removing the first error handler, you allow the error to be caught later on.

A better way (in general) to handle errors in promise chains would be to use a single .catch() at the end of the chain.

Consider this bad code (try running on your browser console):

new Promise(
  function(resolve, reject){
    reject('first');
  }).then(
    function(result) {
      console.log('1st success!', result);
      return result;
    },
    function(err) {
      console.log('1st error!', err);
      return err;
    }
  ).then(
    function(result){
      console.log('2nd success!', result);
    },
    function(err){
      console.log("2nd error!", err);
    }
  );

Output:

1st error! first
2nd success! first

Better way:

new Promise(
  function(resolve, reject){
    reject('first');
  }).then(function(result) {
    console.log('1st success!', result);
    return result;
  }).then(function(result){
    console.log('2nd success!', result);
  // catch error once at the end
  }).catch(function(err){
    console.log("error!", err);
  });

Output:

error! first

Try both of those in browser console, and change reject to resolve to see how it affects the output.

like image 155
mz3 Avatar answered Nov 27 '25 06:11

mz3


add a dependency on the $q and use $q.reject to control the execution...

in your example you need a $q.reject in the aService.error method

as mentioned here in the $q docs

reject(reason);

Creates a promise that is resolved as rejected with the specified reason. This api should be used to forward rejection in a chain of promises. If you are dealing with the last promise in a promise chain, you don't need to worry about it.

When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of reject as the throw keyword in JavaScript. This also means that if you "catch" an error via a promise error callback and you want to forward the error to the promise derived from the current promise, you have to "rethrow" the error by returning a rejection constructed via reject.

like image 34
a14m Avatar answered Nov 27 '25 08:11

a14m



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!