Перейдите в массив отложенных до $.when()
Здесь надуманный пример того, что происходит: http://jsfiddle.net/adamjford/YNGcm/20/
HTML:
<a href="#">Click me!</a>
<div></div>
JavaScript:
function getSomeDeferredStuff() {
var deferreds = [];
var i = 1;
for (i = 1; i <= 10; i++) {
var count = i;
deferreds.push(
$.post('/echo/html/', {
html: "<p>Task #" + count + " complete.",
delay: count
}).success(function(data) {
$("div").append(data);
}));
}
return deferreds;
}
$(function() {
$("a").click(function() {
var deferreds = getSomeDeferredStuff();
$.when(deferreds).done(function() {
$("div").append("<p>All done!</p>");
});
});
});
Я хочу, чтобы все было сделано! появиться после завершения всех отложенных задач, но $.when()
, похоже, не знает, как обрабатывать массив отложенных объектов. "Все сделано!" происходит потому, что массив не является объектом "Отложенные", поэтому jQuery продвигается вперед и предполагает, что он только что сделан.
Я знаю, что можно передать объекты в функцию типа $.when(deferred1, deferred2, ..., deferredX)
, но неизвестно, сколько отложенных объектов там будет выполняться в реальной проблеме, которую я пытаюсь решить.
Ответы
Ответ 1
Чтобы передать массив значений любой функции, которая обычно ожидает их отдельных параметров, используйте Function.prototype.apply
, поэтому в этом случае вам нужно:
$.when.apply($, my_array).then( ___ );
См. http://jsfiddle.net/YNGcm/21/
В ES6 вы можете вместо этого использовать оператор ...
:
$.when(...my_array).then( ___ );
В любом случае, поскольку маловероятно, что вы заранее узнаете, сколько формальных параметров потребует обработчик .then
, этот обработчик должен обработать массив arguments
, чтобы получить результат каждого обещания.
Ответ 2
Обходные пути выше (спасибо!) неправильно решают проблему возврата объектов, предоставленных отложенному методу resolve()
, потому что jQuery вызывает обратные вызовы done()
и fail()
с отдельными параметрами, а не с массивом. Это означает, что мы должны использовать псевдо-массив arguments
, чтобы получить все разрешенные/отклоненные объекты, возвращенные массивом отложенных слов, что является уродливым:
$.when.apply($,deferreds).then(function() {
var objects=arguments; // The array of resolved objects as a pseudo-array
...
};
Поскольку мы прошли в массиве отложенных периодов, было бы неплохо вернуть массив результатов. Также было бы неплохо вернуть реальный массив вместо псевдо-массива, поэтому мы можем использовать такие методы, как Array.sort()
.
Вот решение, основанное на методе when.js when.all()
, который решает эти проблемы:
// Put somewhere in your scripting environment
if (typeof jQuery.when.all === 'undefined') {
jQuery.when.all = function (deferreds) {
return $.Deferred(function (def) {
$.when.apply(jQuery, deferreds).then(
function () {
def.resolveWith(this, [Array.prototype.slice.call(arguments)]);
},
function () {
def.rejectWith(this, [Array.prototype.slice.call(arguments)]);
});
});
}
}
Теперь вы можете просто передать массив отложенных / promises и вернуть массив разрешенных/отклоненных объектов в ваш обратный вызов, например:
$.when.all(deferreds).then(function(objects) {
console.log("Resolved objects:", objects);
});
Ответ 3
Вы можете применить метод when
к вашему массиву:
var arr = [ /* Deferred objects */ ];
$.when.apply($, arr);
Как вы работаете с массивом отложенных jQuery?
Ответ 4
В качестве простой альтернативы, для которой не требуется $.when.apply
или array
, вы можете использовать следующий шаблон для создания единого обещания для нескольких параллельных promises:
promise = $.when(promise, anotherPromise);
например.
function GetSomeDeferredStuff() {
// Start with an empty resolved promise (or undefined does the same!)
var promise;
var i = 1;
for (i = 1; i <= 5; i++) {
var count = i;
promise = $.when(promise,
$.ajax({
type: "POST",
url: '/echo/html/',
data: {
html: "<p>Task #" + count + " complete.",
delay: count / 2
},
success: function (data) {
$("div").append(data);
}
}));
}
return promise;
}
$(function () {
$("a").click(function () {
var promise = GetSomeDeferredStuff();
promise.then(function () {
$("div").append("<p>All done!</p>");
});
});
});
Примечания:
- Я понял это, увидев последовательную цепочку promises, используя
promise = promise.then(newpromise)
- Недостатком является то, что он создает дополнительные объекты обещания за кулисами, и любые параметры, переданные в конце, не очень полезны (поскольку они вложены внутри дополнительных объектов). Для чего вы хотите, хотя это коротко и просто.
- Поверхность - это не требует управления массивами или массивами.
Ответ 5
При вызове нескольких параллельных вызовов AJAX у вас есть два варианта обработки соответствующих ответов.
- Использовать синхронный вызов AJAX/один за другим/не рекомендуется
- Используйте массив
Promises'
и $.when
который принимает promise
и его обратный вызов .done
когда все promise
успешно возвращаются с соответствующими ответами.
пример
function ajaxRequest(capitalCity) {
return $.ajax({
url: 'https://restcountries.eu/rest/v1/capital/'+capitalCity,
success: function(response) {
},
error: function(response) {
console.log("Error")
}
});
}
$(function(){
var capitalCities = ['Delhi', 'Beijing', 'Washington', 'Tokyo', 'London'];
$('#capitals').text(capitalCities);
function getCountryCapitals(){ //do multiple parallel ajax requests
var promises = [];
for(var i=0,l=capitalCities.length; i<l; i++){
var promise = ajaxRequest(capitalCities[i]);
promises.push(promise);
}
$.when.apply($, promises)
.done(fillCountryCapitals);
}
function fillCountryCapitals(){
var countries = [];
var responses = arguments;
for(i in responses){
console.dir(responses[i]);
countries.push(responses[i][0][0].nativeName)
}
$('#countries').text(countries);
}
getCountryCapitals()
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<h4>Capital Cities : </h4> <span id="capitals"></span>
<h4>Respective Country Native Names : </h4> <span id="countries"></span>
</div>
Ответ 6
Я хочу предложить другое с помощью $.each:
-
Мы можем объявить функцию ajax как:
function ajaxFn(someData) {
this.someData = someData;
var that = this;
return function () {
var promise = $.Deferred();
$.ajax({
method: "POST",
url: "url",
data: that.someData,
success: function(data) {
promise.resolve(data);
},
error: function(data) {
promise.reject(data);
}
})
return promise;
}
}
-
Часть кода, где мы создаем массив функций с ajax для отправки:
var arrayOfFn = [];
for (var i = 0; i < someDataArray.length; i++) {
var ajaxFnForArray = new ajaxFn(someDataArray[i]);
arrayOfFn.push(ajaxFnForArray);
}
-
И вызов функций с отправкой ajax:
$.when(
$.each(arrayOfFn, function(index, value) {
value.call()
})
).then(function() {
alert("Cheer!");
}
)
Ответ 7
Если вы транслируете и имеете доступ к ES6, вы можете использовать синтаксис распространения, который специфически применяет каждый повторяемый элемент объекта как дискретный аргумент, просто как ему нужно $.when()
.
$.when(...deferreds).done(() => {
// do stuff
});
MDN Link - Синтаксис распространения
Ответ 8
Если вы используете angularJS или какой-то вариант библиотеки обещаний Q, у вас есть метод .all()
, который решает эту точную проблему.
var savePromises = [];
angular.forEach(models, function(model){
savePromises.push(
model.saveToServer()
)
});
$q.all(savePromises).then(
function success(results){...},
function failed(results){...}
);
см. полный API:
https://github.com/kriskowal/q/wiki/API-Reference#promiseall
https://docs.angularjs.org/api/ng/service/$q
Ответ 9
У меня был случай, очень похожий, когда я отправлял в каждом цикле, а затем устанавливал разметку html в некоторых полях из чисел, полученных из ajax. Затем мне нужно было сделать сумму (обновленных) значений этих полей и поместить в полное поле.
Таким образом, проблема заключалась в том, что я пытался делать сумму на всех числах, но никаких данных еще не было получено из асинхронных вызовов async. Мне нужно было выполнить эту функциональность в нескольких функциях, чтобы повторно использовать код. Моя внешняя функция ожидает данных до того, как я перейду и сделаю некоторые вещи с полностью обновленной версией DOM.
// 1st
function Outer() {
var deferreds = GetAllData();
$.when.apply($, deferreds).done(function () {
// now you can do whatever you want with the updated page
});
}
// 2nd
function GetAllData() {
var deferreds = [];
$('.calculatedField').each(function (data) {
deferreds.push(GetIndividualData($(this)));
});
return deferreds;
}
// 3rd
function GetIndividualData(item) {
var def = new $.Deferred();
$.post('@Url.Action("GetData")', function (data) {
item.html(data.valueFromAjax);
def.resolve(data);
});
return def;
}