1

I have a simple service method that gather several .get() and I'm having some troubles on the "printing" part as by that time I only have one part of the result.

what I'm doing is:

var service = function() {
  var players = []; // will hold 100 objects

  var getMembers = function(id) {
    $.get(url, function(data) {
      for(i=0; i<data.length; i++) {
        var p = data[i];
        // get more info for this member
        getMemberDetails(p.member_id);
        // put the current data into the players
        players.push(p);
      }
    });

    calculateAndPrint();
  };

  var getMemberDetails = function(id) {
    $.get(url, function(data) {
      var result = $.grep(players, function(e){ return e.member_id == id; });
      if (result.length == 0) { /* not found */ } 
      else if (result.length == 1) {
        // push new data to player object
        result[0].details = data;
      }
    });
  };

  var calculateAndPrint = function() {
    for(i=0; i<players.length; i++) {
      var p = players[i];
      console.log(p);
    }
  };
})();

and this does not work, as when I reach calculateAndPrint, the details is not even designed yet...

so I tried $.Deferred() and the only issue I'm having is that if I defer getMemberDetails method, that call includes already a deffer call (the .get()) and I'm back to the same issue ...

what is the best option to only run calculateAndPrint after all 100 calls were made?

It seems easy enough but I'm just blank :/

balexandre
  • 73,608
  • 45
  • 233
  • 342
  • 1
    you could increment a counter and when it reach 100, execute that function inside `getMemberDetails` callback – Hacketo Apr 07 '16 at 12:23
  • 2
    Are you sure you want to make 100+1 http requests for this data? Can't you batch all the 100 requests to get details into one call? – Seth Flowers Apr 07 '16 at 12:24
  • where you are using id and how to are invoking `getMembers()`? – itzmukeshy7 Apr 07 '16 at 12:28
  • @sethflowers no, ServiceAPI does not allow batching, I really need to call 100 times :/ - @hacketo trying to do things right learning the `defer` thingy :) – balexandre Apr 07 '16 at 12:31

2 Answers2

3

This should be pretty easy if you use promises:

var service = function() {
  var getMembers = function(id) {
      return Promise.when($.get("some service url"))
          .then(function (data) {
              return Promise.all(data.map(getMemberDetails));
          });
  };

  var getMemberDetails = function(player) {
      return Promise.when($.get("some service Url?id=" + player.member_id));
  };

  var calculateAndPrint = function(players) {
      players.forEach(function (player) {
          console.log(player);
      });
  };

  return {
      getMembers: getMembers,
      calculateAndPrint: calculateAndPrint
  };
})();

service.getMembers().then(function (players) {
    service.calculateAndPrint(players);
});
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • darn... why was I looking to `$.Deferred()` instead? and good idea to put the chain outside, I was trying just to call `service.getMembers()` and get all at once :) – balexandre Apr 07 '16 at 12:41
-1

you could just create a deferred object $.deferred for every ajax call your making & then wait ($.when) for all those deferred jobs to complete before you run the calculateAndPrint() method.


How It Works:

  • Create a deferred object for every ajax call $.deferred & return the promise object .promise().
  • depending on whether ajax call is successful or not , either resolve with response data .resolve(responseData) or reject with error data .reject(errorData).
  • Monitor all the ajax calls by there promise objects returned from step1 and on completion , call the calculateAndPrint() method.
  • For arbitrary ajax calls most of the above logic remains same,except that those are called in for loop and each of those deferred calls are pushed into a deferredCalls array.

Note:I would suggest to show some loader/spinner image when your making ajax calls, since you would not get the response immediately & keeping user informed about the background operation is always good User experience sign.

JS CODE:

/* utils */
var $ul = $('ul');

function msg(text) {
  $ul.append('<li>' + text + '</li>'); 
}

/* functions */
function asyncThing1() {
  var dfd = $.Deferred();
  setTimeout(function() {
    msg('asyncThing1 seems to be done...');
    dfd.resolve('banana');
 }, 1000);
 return dfd.promise();
}

function asyncThing2() {
  var dfd = $.Deferred();
    setTimeout(function() {
    msg('asyncThing2 seems to be done...');
    dfd.resolve('apple');
  }, 500);
  return dfd.promise();
}

function asyncThing3() {
  var dfd = $.Deferred();
  setTimeout(function() {
    msg('asyncThing3 seems to be done...');
    dfd.resolve('orange');
  }, 1500);
  return dfd.promise();
}

/* do it */
$.when(asyncThing1(), asyncThing2(), asyncThing3()).done(function(res1, res2, res3) {
  msg('all done!');
  msg(res1 + ', ' + res2 + ', ' + res3);
});

Live Demo @ JSFiddle


Arbitrary Deferred calls Original SO Post :

//Push all arbitrary ajax calls to deferred array
var deferreds = [];
function getSomeDeferredStuff() {
   var i = 1;
   for (i = 1; i <= 10; i++) {
      var count = i;
      deferreds.push(
        $.post('/echo/html/', {
          html: "<p>Task #" + count + " complete.",
          delay: count
        }).success(function(data) {
           $("div").append(data);
      }));
   }
}


// define a extension method for $.when for creating/managing deferred
// objects for every ajax call
if (jQuery.when.all===undefined) {
   jQuery.when.all = function(deferreds) {
      var deferred = new jQuery.Deferred();
      $.when.apply(jQuery, deferreds).then(
          function() {
             var deferredObjs= function (arguments) { return deferreds.length > 1 ? $.makeArray(arguments) : [arguments]; }
             deferred.resolve(deferredObjs);
          },
          function() {
             deferred.fail(deferredObjs);
          });

      return deferred;
   }
}

//passing the deferred calls array to $.when
$.when.all(deferreds).then(function(objects) {
    //process when all deferred objects compelted
    console.log("Resolved/rejected objects:", objects);
});

Working example for arbitrary ajax calls @JSFiddle

Community
  • 1
  • 1
dreamweiver
  • 6,002
  • 2
  • 24
  • 39
  • OP's question is about waiting on an arbitrary (i.e. not pre-determined) number of asynchronous operations after performing another asynchronous operation. Your answer shows how to wait on a specific number of parallel operations, so it does not answer OP's question. – JLRishe Apr 09 '16 at 15:33
  • well this was just syntax to make deferred calls and get notified when all are done.In order to make arbitrary deferred calls, one must just create a deferred array in a loop and then pass that deferred array to `$.when.apply`, so nothing much changes in the above solution when making arbitrary deferred calls.anyway for better understanding i`ll edit the answer. – dreamweiver Apr 10 '16 at 05:54