Я пытаюсь получить общую сумму от возврата нескольких запросов ajax, я использую синхронизацию прямо сейчас, чтобы заставить ее работать, так как я думаю, что это было бы гораздо лучшим решением.

Вот мой кофе

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

Что хорошо компилируется в

  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;
  };

Есть ли способ получить полную работу без использования синхронных js.

Я экспериментировал с $ .when (). Then (), но он вызывает проблемы, когда размер запросов является динамическим.

1
austinbv 25 Авг 2011 в 19:46

3 ответа

Лучший ответ

Я не знаю CoffeeScript, поэтому вот чистое решение jQuery:

Вы не можете вернуть значение из get_total без синхронных вызовов. Что вы можете сделать, так это вызвать обратный вызов после завершения всех запросов.

В этом примере используются объекты Deferred [docs] < / sup> :

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);
    });
}

Применение:

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

Если вам нужно get_total, чтобы вернуть значение, тогда вы должны выполнять синхронные вызовы. В общем, это плохая идея, особенно когда вы делаете несколько запросов. Это заморозит пользовательский интерфейс браузера. Лучше реструктурировать свой код для работы с обратными вызовами.

Обновление: я только что прочитал последнее предложение вашего вопроса. Вы можете передать динамическое количество аргументов функции, используя .apply() [MDN] .

Обновление2: Конечно, если вы контролируете службу, вы должны заставить ее принимать несколько ключевых слов, чтобы избежать множественных запросов Ajax.

4
Felix Kling 25 Авг 2011 в 18:43

Конечно, но вам нужно добавить отслеживание состояния, чтобы выяснить, когда все запросы ajax вернулись, а затем вызвать функцию для передачи сгенерированного итога. Если вы переключитесь на асинхронные запросы, то «total» будет возвращено сразу после того, как будут запущены запросы ajax, и к этому времени, возможно, будет возвращено только 0 или пара запросов, поэтому вы получите неправильную сумму.

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
          }
      }
   });
}
1
Marc B 25 Авг 2011 в 15:52

Я не знаю coffeescript, но для этого в js можно использовать закрытие:

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);
            }
        }
    };
})();

Определите это как переменную, к которой может обращаться ваш обратный вызов, а затем в обратном вызове ajax выполните:

total_trend.add_to_total(data.response.total);

И когда вы хотите получить общую сумму:

total_trend.get_total();

И если вы хотите отложить общую сумму до тех пор, пока add_to_total не получит заданное количество вызовов:

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

В приведенном выше случае функция обратного вызова будет вызвана, когда add_to_total будет вызван 5 раз.


РЕДАКТИРОВАТЬ: как указал Феликс, исходная версия не поддерживала ожидание, пока не будут выполнены вызовы ajax. Код был обновлен, чтобы поддерживать отсрочку итоговой суммы. Это должно сработать, но ответ Феликса, вероятно, на данный момент немного чище.

1
Chris Pickett 25 Авг 2011 в 16:28