Вот надуманный пример того, что происходит: 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)
но неизвестно, сколько отложенных объектов будет при выполнении в реальной задаче, которую я пытаюсь решить.
javascript
jquery
argument-passing
jquery-deferred
.when
adamjford
источник
источник
$.when.apply
вообще получать тот же результат.Ответы:
Чтобы передать массив значений любой функции, которая обычно ожидает, что они будут отдельными параметрами, используйте
Function.prototype.apply
, поэтому в этом случае вам нужно:Смотрите http://jsfiddle.net/YNGcm/21/
В ES6 вы можете использовать вместо этого
...
оператор распространения :В любом случае, поскольку маловероятно, что вы заранее будете знать, сколько формальных параметров
.then
потребуется обработчику, этот обработчик должен будет обработатьarguments
массив, чтобы получить результат каждого обещания.источник
$.when
-f.apply(ctx, my_array)
будет называтьf
сthis == ctx
и аргументами , установленных на содержание вmy_array
.$
vsnull
в качестве первого параметра стоит прочитать. В данном конкретном случае это не имеет значения.$
это меньше, чем печатать,null
и вы безопасны при$.when
изменении реализации (не то, чтобы это было вероятно в этом случае, но почему бы не оставитьthis
неизменным по умолчанию).Обходные пути, описанные выше (спасибо!), Не решают должным образом проблему возврата объектов, предоставленных
resolve()
методу deferred, потому что jQuery вызывает обратные вызовыdone()
иfail()
с отдельными параметрами, а не массивом. Это означает, что мы должны использоватьarguments
псевдомассив, чтобы получить все разрешенные / отклоненные объекты, возвращаемые массивом deferreds, что ужасно:Поскольку мы передали массив отсрочек, было бы неплохо вернуть массив результатов. Также было бы неплохо получить реальный массив вместо псевдомассива, чтобы мы могли использовать такие методы, как
Array.sort()
.Вот решение, основанное на методе when.js ,
when.all()
который решает эти проблемы:Теперь вы можете просто передать массив отсроченных / обещаний и вернуть массив разрешенных / отклоненных объектов в вашем обратном вызове, например, так:
источник
var toArray = function (args) { return deferreds.length > 1 ? $.makeArray(args) : [args]; }
вместоArray.prototype.slice.call
.Вы можете применить
when
метод к вашему массиву:Как вы работаете с массивом jQuery Deferreds?
источник
При вызове нескольких параллельных вызовов AJAX у вас есть два варианта обработки соответствующих ответов.
Promises'
массив,$.when
который принимаетpromise
s, и его обратный вызов.done
вызывается, когда всеpromise
s возвращаются успешно с соответствующими ответами.пример
источник
$.when
.for ... in
для массива ?!)(not recommended)
2. Не согласен -for ... in
все в порядке, потому что массив содержит только те свойства, которые нужны (без дополнительных свойств). спасибо в любом случаеArray.prototype
. В любом случае, для не критичного к производительности кода было бы лучше использовать.map
вместоfor
/push
loop, напримерvar promises = capitalCities.map(ajaxRequest); $.when.apply($, promises).then(fillCountryCapitals)
- работа выполнена.В качестве простой альтернативы, которая не требует
$.when.apply
илиarray
, вы можете использовать следующий шаблон для генерации одного обещания для нескольких параллельных обещаний:например
Ноты:
promise = promise.then(newpromise)
источник
then()
аналогичным образом для цепочки вызовов. Поведение с$.when
должно действовать как оно параллельно (не сковано). Пожалуйста, попробуйте, прежде чем выбросить полезную альтернативу, так как она работает :)Я хочу предложить другой с использованием $ .each:
Мы можем объявить функцию ajax как:
Часть кода, где мы создаем массив функций с помощью ajax для отправки:
И вызов функций с отправкой ajax:
источник
Если у вас есть доступ к ES6, вы можете использовать распространенный синтаксис, который конкретно применяет каждый повторяемый элемент объекта в качестве отдельного аргумента, так, как это
$.when()
необходимо.MDN Link - Синтаксис распространения
источник
Если вы используете angularJS или какой-либо вариант библиотеки Q обещаний, то у вас есть
.all()
метод, который решает эту проблему.увидеть полный API:
https://github.com/kriskowal/q/wiki/API-Reference#promiseall
https://docs.angularjs.org/api/ng/service/$q
источник
.map
здесь, ну да ладно).У меня был очень похожий случай, когда я публиковал в каждом цикле, а затем устанавливал html-разметку в некоторых полях из чисел, полученных из ajax. Затем мне нужно было сделать сумму (теперь обновленных) значений этих полей и поместить их в общее поле.
Таким образом, проблема заключалась в том, что я пытался подсчитать сумму по всем числам, но данные от асинхронных вызовов ajax еще не вернулись. Мне нужно было завершить эту функцию в несколько функций, чтобы иметь возможность повторно использовать код. Моя внешняя функция ждет данных, прежде чем я пойду и сделаю кое-что с полностью обновленным DOM.
источник