3

I have the following service method:

ResourcesService.prototype.list = function ()
{
      var deferred = q.defer();
      var settings = fetchSettings();
      restService.getAll(resourceName, settings)
      .then(function (response) {
          deferred.resolve(response.data, {
               count: response.headers('cr_count'),
               total: response.headers('cr_total'),
               last: response.headers('cr_last')
          });
      }, function (error) {
          deferred.reject(error.statusText);
      });
      return deferred.promise;
}

As you can see I am passing two values to deferred.resolve, which are response.data and a metadata object.

Up in the call stack I have:

//"scenes" is an object that inherits from ResourcesService
scenes
.withLanguage('en-us')
.sort('creation')
.size(2)
.list()
.then(function (firstPage, metadata) {
     //firstPage is the "response.data" from previous method
     //metadata is undefined, but should be an object with all the values from the headers
});

Why is metadata undefined? I debugged ResourcesService and the headers are being read just fine, but the object passed is as argument to deferred.resolve is not being delegated to my callback function.

Does deferred.resolve support only one argument to be passed to the callback? Do I have to put this metadata in the same object along with the response?

Matias Cicero
  • 25,439
  • 13
  • 82
  • 154
  • 1
    Avoid the [deferred antipattern](http://stackoverflow.com/q/23803743/1048572). – Bergi Sep 10 '15 at 15:14
  • See also [javascript promise not passing all arguments (using `Q`)](http://stackoverflow.com/q/17970420/1048572) (`$q` works the same in that regard) – Bergi Sep 10 '15 at 15:16

2 Answers2

3

You can't pass more then one parameter into then callback, only the one is expected and considered. What you can do however is to resolve your promise with an object. For example:

ResourcesService.prototype.list = function () {

    var settings = fetchSettings();
    return restService.getAll(resourceName, settings).then(function (response) {
        return {
            data: response.data, 
            metadata: {
                count: response.headers('cr_count'),
                total: response.headers('cr_total'),
                last: response.headers('cr_last')
            }
        };    
    }, function (error) {
        throw new Error(error.statusText);
    });
}

Note, that I also fixed deferred anti-pattern in your code, you don't need dummy deferred object, because you already have promise you can return.

Then you would use it like this:

scenes
.withLanguage('en-us')
.sort('creation')
.size(2)
.list()
.then(function (response) {
    var firstPage = response.data,
        metadata =  response.metadata;
});
dfsq
  • 191,768
  • 25
  • 236
  • 258
  • This approach is much nicer than mine, but why are you throwing an exception when there is an error? I tried returning `error.statusText` but my error callback is never executed. Instead, my success callback is called. How can I call the error callback without using `q.reject`? – Matias Cicero Sep 10 '15 at 16:20
  • This is exactly why I'm throwing: because if you return - it means that you handled failure, so sort of recovered and then in subsequent callbacks you will not get into error handler - because you already handled error. So in this case if you want to propagate erro down the chain you either $q.reject or throw. You can even just `throw error.statusText;` if you prefer. – dfsq Sep 10 '15 at 16:23
  • I see. Thank you for your answer!. I ended up doing it your way and I like it more than before, plus I no longer depend on `$q` service. – Matias Cicero Sep 10 '15 at 18:01
1

While @dsfq is right about not resolving with more than one argument, if you're using q, you could also wrap your resolved values in an array and use .spread() instead of .then() to split them across arguments.

Created:

  .then(function (response) {
      // Resolve with a single array
      deferred.resolve([response.data, {
           count: response.headers('cr_count'),
           total: response.headers('cr_total'),
           last: response.headers('cr_last')
      }]);
  }

Consumed:

scenes
.withLanguage('en-us')
.sort('creation')
.size(2)
.list()
// .spread() instead of .then()
.spread(function (firstPage, metadata) {
  // Works as expected
});
dthareja
  • 146
  • 1
  • 4