开发者

Getting the total from multiple ajax requests

I am trying to get a total from the return of multiple ajax requests, I use sync right now to make it work, since I think that would be a much better solution.

Here is my Coffee

get_total = (trend, duration) ->
  total = 0
  for keyword in trend.search_terms
    url = "http://otter.topsy.com/search.json?q=#{keyword}&window=#{duration}"
    $.ajax
      url: url
      async: false
      success: (data) ->
        total += data.response.total
  total

Which compiles nicely to

  get_total = function(trend, duration) {
    var keyword, total, url, _i, _len, _ref;
    total = 0;
    _ref = trend.search_terms;
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
      keyword = _ref[_i];
      url = "http://otter.topsy.com/search.json?q=" + keyword + "&window=" + duration;
      $.ajax({
        url: url,
        async: false,
        success:开发者_开发知识库 function(data) {
          return total += data.response.total;
        }
      });
    }
    return total;
  };

Is there a way to have the total work without using synchronous js.

I have been experimenting with the $.when().then() but it is causing issues when the size of the requests is dynamic.


I don't know CoffeeScript, so here is a pure jQuery solution:

You cannot return a value from get_total without making synchronous calls. What you can do is call a callback, once all the requests have finished.

This example makes use of jQuery's Deferred objects [docs]:

function get_total(trend, duration, callback) {
    var deferreds = [], total = 0;

    for(var i = 0, l = trend.search_terms.length; i < l; i++) {
        deferreds.push($.get("http://otter.topsy.com/search.json?q=" + trend.search_terms[i] + "&window=" + duration, function(data) {
            total += data.response.total;
        }));
    }

    $.when.apply($, deferreds).then(function() {
        callback(total);
    });
}

Usage:

get_total(trend, 200, function(total) {
   // now execute the code that needs `total`
});

If you need get_total to return the value, then you have to make synchronous calls. In general this is a bad idea, especially when you are making multiple requests. It will freeze the browser's UI. Better you restructure your code to work with callbacks.

Update: I just read the last sentence of your question. You you can pass a dynamic number of arguments to a function using .apply() [MDN].

Update2: Of course if you have control over the service, you should make it accept multiple keywords to avoid multiple Ajax requests.


Sure, but you'll need to add some state tracking to figure out when all the ajax requests have returned, then call a function to pass in the generated total. If you switch to async requests, then 'total' will be returned immediately after the ajax requests are fired, by which time maybe only 0 or a couple requests have returned, so you'll get an incorrect total.

var requestsOutstanding = 0;
var total = 0;
for (i = 0; i < _ref.length) {
   keyword = _ref[i];
   url = "...";
   requestsOutstanding++;
   $.ajax({
      url: url,
      async: true,
      success: function(data) {
          total += data.response.total;
          requestsOutstanding--;
          if (requestsOutstanding == 0) {
             totalResultIsAvailable(total); // trigger next stage of events here
          }
      }
   });
}


I don't know coffeescript, but to do this in js you could use a closure:

var total_trend = (function() {
    var total = 0,
        num_adds = 0;

    return {
        add_to_total: function(add) { total += parseInt(add, 10); num_adds += 1; },
        get_total: function() { return total; },
        get_deferred_total: function(expected, callback) {
            if(num_adds !== expected) {
                var _this = this;
                setTimeout(function(){ return _this.get_deferred_total(expected, callback); }, 100);
            } else {
                callback(total);
            }
        }
    };
})();

Define this as a variable your callback can access, then in the ajax callback do:

total_trend.add_to_total(data.response.total);

and when you want the total:

total_trend.get_total();

and if you want to defer the total until add_to_total has had a given amount of calls:

var expected_num_calls = 5;
total_trend.get_deferred_total(expected_num_calls, function(total) { alert(total); } )

In the case above, the callback function will get called when add_to_total has been called 5 times.


EDIT: As pointed out by Felix the original version didn't support waiting till the ajax calls were done. The code has been updated to support deferring the total. This should work, but Felix's answer is probably a bit cleaner at this point.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜