1

I use Q.js to call an api use two loops as follows in my main function

    for i..10
     for i...5
         var promise = getLoc(x,y);
         promise.then(function(value) {
             //value is undefined...
         }

In my getLoc function I have

    function getLoc(x,y) {
      var value;
      var deferred = Q.defer();
      Q.ninvoke(request, 'get', {

      }).spread(function(response, body) {
        value = body;
      });

      defferred.resolve(value);
      return deferred.promise
    }

Why is my value that is passed to the then's function not defined? How do I pass my resolved value? Thanks!

jaz wu
  • 11
  • 3
  • Just avoid the [deferred antipattern](http://stackoverflow.com/q/23803743/1048572) and the problem will go away by itself! – Bergi Mar 23 '16 at 23:19
  • 1
    Possible duplicate of [Resolve promises one after another (i.e. in sequence)?](http://stackoverflow.com/questions/24586110/resolve-promises-one-after-another-i-e-in-sequence) – Tamas Hegedus Mar 23 '16 at 23:30
  • It's correct behaviour. At the point you call `defferred.resolve(value);` the `value` is undefined. – Michał Miszczyszyn Mar 23 '16 at 23:38

1 Answers1

3

You have to call resolve() inside the async callback because that callback is called sometime in the future so that's the only place the value is known:

function getLoc(x,y) {
  var deferred = Q.defer();
  Q.ninvoke(request, 'get', {  // ... other options here
  }).spread(function(response, body) {
     deferred.resolve(body);
  });

  return deferred.promise;
}

You were attempting to return the value before the async callback had even been called and thus is was always undefined.

Or, since it appears that Q.ninvoke() returns a promise, you can just do this and avoid a common deferred anti-pattern:

function getLoc(x,y) {
  return Q.ninvoke(request, 'get', {  // ... other options here
  }).spread(function(response, body) {
     return body;
  });
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I hope you did identify it as the antipattern that it is and are already working on a proper solution… – Bergi Mar 23 '16 at 23:20
  • Well I judged you knew a bit about promises, and they're basically all the same :-) [Bluebird docs on `spread`](http://bluebirdjs.com/docs/api/spread.html)? – Bergi Mar 23 '16 at 23:25
  • 1
    @Bergi - I added a better way. I don't know Q and don't intend to learn it so I just went to first the obvious mistake, but now I see that apparently `Q.ninvoke()` returns a promise so that can just be returned. – jfriend00 Mar 23 '16 at 23:25
  • @jfriend00 @Bergi I realized that after I moved `deferred.resolve(body);` inside as I have above in my question I was getting the requests returned out of order because I also passed in the `i` value in my loop to the API GET request in my `getLoc(x,y,i)`, and I also added the `i` in my `resolve()` but it returned out of order `i` values. I need it to be sequential, how can I do this while also being able to return my `body' value from my get request in `myLoc()` ? – jaz wu Mar 23 '16 at 23:28
  • @jazwu - There are a number of different techniques to either sequence async operations or to collect results in the original order, even while running parallel. That appears to be a different question (we've answered your original question now, I think). I'd suggest you search on sequencing promise operations as there are lots of things already written. If you get stuck there, we'd need to see more of your loop code to see what is being sequenced and you could post a new question with that code in it and a fuller description of the new problem. – jfriend00 Mar 23 '16 at 23:29
  • you can use `Q.all()` after having pushed every request in an array inside the for loop. – morels Mar 24 '16 at 10:45