jQuery, $ .ajax с массивом URL

9

У меня есть простой массив URL-адресов, и я хочу загрузить каждый из них с помощью jQuery. Я использовал $.get, но я не могу заставить его работать с $.Deferred, поэтому я переключился на $.ajax - у меня почти есть работа, но результаты, которые я получаю, нечетные. Я надеялся, что кто-то поможет мне улучшить эту работу.

var results = [], files = [
   'url1', 'url2', 'url3'
];

$.when(
   $.ajax(files[0]).done(function(data) { 
      results.push(data); console.log("step 1.0"); 
   }),
   $.ajax(files[1]).done(function(data) { 
      results.push(data); console.log("step 1.1"); 
   }),
   $.ajax(files[2]).done(function(data) {
      results.push(data); console.log("step 1.2"); 
   })
).then(function(){
   console.log("step 2");
});

Это должно выводиться..

  • шаг 1.0
  • шаг 1.1
  • шаг 1.2
  • шаг 2

И затем массив results содержит результат всех трех запросов ajax. Возможно ли это?

  • 1
    В чем проблема у вас с этим? Запросы могут выполняться в другом порядке, поэтому вы можете выполнить step 1.2, step 1.0, step 1.1, step 2 , но массив всегда будет заполнен до выполнения then() .
  • 0
    Мне абсолютно необходимо, чтобы они были выполнены в правильном порядке. Важно, чтобы код работал правильно.
Показать ещё 7 комментариев
Теги:
arrays
promise
jquery-deferred

2 ответа

8
Лучший ответ

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

При использовании $.when() вы запускаете все три вызова ajax параллельно. Если вы изучите результаты только после того, как все они будут выполнены, вы можете обработать результаты в определенном порядке (так как вы будете обрабатывать их только тогда, когда все результаты будут доступны, и они будут доступны в запрошенном порядке). Но, делая это таким образом, все вызовы ajax будут изначально отправляться сразу. Это даст вам лучшее время от времени до конца, поэтому, если это возможно для типов запросов, это, как правило, лучший способ сделать это.

Чтобы сделать это, вы можете реструктурировать то, что у вас есть, примерно так:

Запуск в параллельном режиме

var files = [
   'url1', 'url2', 'url3'
];

$.when($.ajax(files[0]),$.ajax(files[1]),$.ajax(files[2])).done(function(a1, a2, a3) {
   var results = [];
   results.push(a1[0]);
   results.push(a2[0]);
   results.push(a3[0]);
   console.log("got all results")
});

Поскольку вы ожидаете, пока обработчик .done() для $.when() был вызван, все результаты ajax будут готовы сразу, и они будут представлены $.when() в том порядке, в котором они были запрошены (независимо от того, какой из них фактически законченный первым), поэтому вы получаете результаты как можно быстрее и представлены в предсказуемом порядке.

Заметьте, я также переместил определение массива results в обработчик обработчика $.when(), потому что единственное место, где вы знаете, данные действительно действительны (по причинам времени).


Запуск в параллельном режиме - Итерация произвольной длины массива

Если у вас был более длинный массив, вам может показаться лучше итерации через массив с чем-то вроде .map(), чтобы обрабатывать их все в цикле, а не перечислять их отдельно:

var files = [
   'url1', 'url2', 'url3', 'url4', 'url5', 'url6', 'url7'
];

$.when.apply($, files.map(function(url) {
    return $.ajax(url);
})).done(function() {
    var results = [];
    // there will be one argument passed to this callback for each ajax call
    // each argument is of this form [data, statusText, jqXHR]
    for (var i = 0; i < arguments.length; i++) {
        results.push(arguments[i][0]);
    }
    // all data is now in the results array in order
});

Последовательность вызовов Ajax

Если, с другой стороны, вы действительно хотите упорядочить свои вызовы ajax, чтобы второй не запускался до тех пор, пока первый не завершится (что может потребоваться, если 2-й вызов ajax требует результатов от первого вызова ajax в чтобы узнать, что нужно запросить или сделать), тогда вам нужен совершенно другой шаблон дизайна, а $.when() - это вовсе не путь (он выполняет только параллельные запросы). В этом случае вы, вероятно, просто хотите связать свои результаты с помощью x.then().then(), и затем вы можете вывести операторы журнала в запрошенной вами последовательности.

  $.ajax(files[0]).then(function(data0) {
      console.log("step 1.0");
      return $.ajax(files[1]);
  }).then(function(data1) {
      console.log("step 1.1");
      return $.ajax(files[2]);
  }).done(function(data2) {
      console.log("step 1.2");
      // all the ajax calls are done here
      console.log("step 2");
  });

Выход консоли:

step 1.0
step 1.1
step 1.2
step 2

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

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


Последовательность вызовов Ajax - Итерация произвольной длины Array

Здесь последовательность, которая будет выглядеть в цикле:

var files = [
   'url1', 'url2', 'url3', 'url4', 'url5', 'url6', 'url7'
];

var results = [];
files.reduce(function(prev, cur, index) {
    return prev.then(function(data) {
        return $.ajax(cur).then(function(data) {
            console.log("step 1." + index);
            results.push(data);
        });
    })
}, $().promise()).done(function() {
    // last ajax call done
    // all results are in the results array
    console.log("step 2.0");
});

Выход консоли:

step 1.0
step 1.1
step 1.2
step 1.3
step 1.4
step 1.5
step 1.6
step 2

Метод Array.prototype.reduce() работает здесь удобно, потому что он накапливает одно значение при обработке каждого отдельного элемента массива, что вам нужно делать, когда вы добавляете .then() для каждого элемента массива. Итерация .reduce() запускается с пустым/разрешенным обещанием с $().promise() (есть и другие способы создания такого обещания), который просто дает нам что-то, чтобы начать делать .then() на том, что уже разрешено.

  • 0
    Это очень полезно. Я начинаю видеть мудрость в использовании внешней библиотеки, даже с учетом возможных накладных расходов. Прямо сейчас мне нужно сделать это только в одном месте, но если мне нужно сделать это два или более раз, объем дополнительной работы, чтобы заставить его функционировать, становится более разрушительным.
  • 0
    @Ciel - я не понимаю твой комментарий. Зачем вам нужна внешняя библиотека? Вы можете превратить любую из этих опций в повторно используемую функцию с помощью всего лишь нескольких строк кода.
Показать ещё 3 комментария
2

Вам нужно получить доступ к возвращаемым значениям из. then вместо каждого .done. Кроме того, .map является вашим другом.

var results = [], files = [
   'url1', 'url2', 'url3'
];

$.when.apply($, $.map(files, function (file) {
    return $.ajax(file);
})).then(function (dataArr) {
    /* 
     * dataArr is an array of arrays, 
     * each array contains the arguments 
     * returned to each success callback
     */
    results = $.map(dataArr, function (data) {
        return data[0]; // the first argument to the success callback is the data
    });
    console.log(results);
});

аргументы, переданные. then, будут в том же порядке, в каком они были переданы. Когда

  • 1
    Я не думаю, что вы обрабатываете данные из $.when() правильно. Он не дает вам ни одного аргумента, который представляет собой массив массивов. Он дает вам серию аргументов, где каждый аргумент представляет собой массив из трех значений. Смотрите пример кода со второго по последний здесь api.jquery.com/jquery.when в строке 3.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню