У меня есть простой массив 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");
});
Это должно выводиться..
И затем массив results
содержит результат всех трех запросов ajax. Возможно ли это?
Во-первых, вам нужно решить, хотите ли вы, чтобы ваши три вызова 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()
на том, что уже разрешено.
Вам нужно получить доступ к возвращаемым значениям из. 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, будут в том же порядке, в каком они были переданы. Когда
$.when()
правильно. Он не дает вам ни одного аргумента, который представляет собой массив массивов. Он дает вам серию аргументов, где каждый аргумент представляет собой массив из трех значений. Смотрите пример кода со второго по последний здесь api.jquery.com/jquery.when в строке 3.
step 1.2, step 1.0, step 1.1, step 2
, но массив всегда будет заполнен до выполненияthen()
.