2016-07-11 6 views
1

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

  1. выполнить цикл for различной длины, который извлекает строки таблицы через запрос ajax.
  2. как только у меня есть все строки, я хочу получить статистику, через другой запрос ajax
  3. после того, как у меня есть статистика, мне нужно отправить сообщение на сервер, который выполняет другой вызов бесплатной базы данных память (о да, я общаюсь с каким-то старым веб-сервисом за всем этим).

Вот мой код:

var chainedPromise = new Array(); 
    //Build a .then() chain by assignment 
    for (i = 0; i < requests.length; i++) { 
     (function(i){ 
      chainedPromise = $.when().then(function(){ 
       return get_results_ajax(table, idRows, requests[i-1], requests[i], type, cfg.siteroot); 
      }); 
     })(i); 
    } 

    var Promise = $.when(chainedPromise).then(function(){ 
     return get_stats_ajax(table, idRows, type, cfg.siteroot); 
    }).then(function() { 
     return get_free_ajax(table,idRows, type, cfg.siteroot); 
    }); 

запросов содержит список номеров: [100, 200, 300, 400] до 5000. Каждый запрос должен отправить обратно 100 строк таблицы. Это работает правильно.

Не работает то, что статистика и свободная операция выполняются ПОСЛЕ того, как я получил все данные. Таким образом, цепочка последних двух вызовов должна быть выполнена по-разному, очевидно, но я не понимаю, как именно это должно быть сделано.

Аякса звонки сделаны так:

function get_results_ajax(table, idRows, start, stop, type, siteroot) { 
    var deferred = jQuery.Deferred(); 
    var request = jQuery.ajax({ 
     type  : 'POST' 
     url   : siteroot+"/index.php?page=ajax", 
     data  : "type="+type+"&idRows="+idRows+"&start="+start+"&stop="+stop, 
     dataType : 'json', 
    }); 

    request.success(function(data) { 
     table.rows.add(data).draw(); 
    }); 

    request.done(function(msg, textStatus, jqXHR) { 
     if (jqXHR.status === 200) { 
      deferred.resolve(jqXHR.responseText); 
     } 
     console.log("Returned data from " + start + " to " + stop); 
    }); 

    request.fail(function(msg, textStatus, jqXHR) { 
     console.log("The server is taking too long to respond. Start: " + start + " Stop: " + stop); 
     deferred.reject("HTTP error: " + jqXHR.status); 
    }); 
    return deferred.promise(); 
} 
+0

Ваши 1, 2 и 3 звучат как [Reactive Extensions.] (Https://github.com/Reactive-Extensions/RxJS) с некоторыми пользовательскими функциями ajax. –

ответ

0

Во-первых, есть несколько ошибок в здесь:

var chainedPromise = new Array(); 
//Build a .then() chain by assignment 
for (i = 0; i < requests.length; i++) { 
    (function(i){ 
     chainedPromise = $.when().then(function(){ 
      return get_results_ajax(table, idRows, requests[i-1], requests[i], type, cfg.siteroot); 
     }); 
    })(i); 
} 

var Promise = $.when(chainedPromise).then(function(){ 
    return get_stats_ajax(table, idRows, type, cfg.siteroot); 
}).then(function() { 
    return get_free_ajax(table,idRows, type, cfg.siteroot); 
}); 
  1. chainedPromise является массивом, и вы хотите, чтобы оставаться массив, чтобы вы могли передать его $.when(). Чтобы добавить значение в массив, вы используете .push(). Вы просто назначаете chainedPromise, который просто заменяет его новым значением и больше не является массивом.

  2. get_results_ajx() возвращает обещание, поэтому вы можете просто использовать это обещание напрямую. Не нужно использовать $.when().then(...) перед ним.

  3. $.when() не принимает массив непосредственно (глупое проектное решение по Jquery, если вы спросите меня), а принимает последовательность отдельных аргументов, поэтому передать ей массив, вы должны использовать .apply() превратить массив в список аргументов.

  4. Когда у вас есть массив, который вы хотите обработать в массив обещаний, гораздо проще использовать .map(), чем цикл for.

  5. У вас есть цикл с индексом массива, который начинается с 0, и вы пытаетесь ссылаться requests[i-1], который даст вам undefined на первой итерации цикла, поскольку i-1 === -1. Я не знаю, что вы намеревались для этой первой итерации. Вы хотите начать свою итерацию в i = 1?

Таким образом, за исключением того, что вам еще нужно исправить пункт 5 выше, все, что исходный код может быть изменен следующим образом:

var promiseArray = requests.map(function(item, i) { 
    return get_results_ajax(table, idRows, requests[i-1], requests[i], type, cfg.siteroot); 
}); 

$.when.apply($, promiseArray).then(function() { 
    return get_stats_ajax(table, idRows, type, cfg.siteroot); 
}).then(function() { 
    return get_free_ajax(table,idRows, type, cfg.siteroot); 
}); 

Тогда ваш get_results_ajax() делает вещи гораздо сложнее, чем требуется. jQuery's $.ajax() уже возвращает обещание. Вы можете использовать его напрямую, и вам не нужно создавать свои собственные отложенные. Фактически, обертывание его в другом отложенном называется promise anti-pattern.

Итак, это:

function get_results_ajax(table, idRows, start, stop, type, siteroot) { 
    var deferred = jQuery.Deferred(); 
    var request = jQuery.ajax({ 
     type  : 'POST' 
     url   : siteroot+"/index.php?page=ajax", 
     data  : "type="+type+"&idRows="+idRows+"&start="+start+"&stop="+stop, 
     dataType : 'json', 
    }); 

    request.success(function(data) { 
     table.rows.add(data).draw(); 
    }); 

    request.done(function(msg, textStatus, jqXHR) { 
     if (jqXHR.status === 200) { 
      deferred.resolve(jqXHR.responseText); 
     } 
     console.log("Returned data from " + start + " to " + stop); 
    }); 

    request.fail(function(msg, textStatus, jqXHR) { 
     console.log("The server is taking too long to respond. Start: " + start + " Stop: " + stop); 
     deferred.reject("HTTP error: " + jqXHR.status); 
    }); 
    return deferred.promise(); 
} 

Может стать это:

function get_results_ajax(table, idRows, start, stop, type, siteroot) { 
    return jQuery.ajax({ 
     type  : 'POST' 
     url   : siteroot+"/index.php?page=ajax", 
     data  : "type="+type+"&idRows="+idRows+"&start="+start+"&stop="+stop, 
     dataType : 'json', 
    }).then(function(data) { 
     table.rows.add(data).draw(); 
    }); 
}  

Что я изменил:

  1. Верните jQuery.ajax() обещание непосредственно. Нет необходимости создавать отложенные.
  2. Использовать стандарт обещаний .then() вместо проприетарного устройства jQuery .done(). Хорошо, что делать дальше, поскольку jQuery поддерживает .then(), и теперь это стандарт в ES6.
  3. При возврате обещания напрямую, он автоматически распространяет ошибки в возвращенном обещании. Вам не нужно делать это вручную.