这是一个人为的例子: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()
似乎不知道如何处理延迟对象数组。 “全部完成!”首先发生的是因为数组不是Deferred对象,所以jQuery继续并且假定它刚刚完成。
我知道可以将对象传递给像$.when(deferred1, deferred2, ..., deferredX)
这样的函数,但不知道在我试图解决的实际问题中将执行多少个Deferred对象。
答案 0 :(得分:694)
要将值数组传递给通常希望它们是单独参数的任何函数,请使用Function.prototype.apply
,因此在这种情况下,您需要:
$.when.apply($, my_array).then( ___ );
请参阅http://jsfiddle.net/YNGcm/21/
在ES6中,您可以改为使用...
spread operator:
$.when(...my_array).then( ___ );
在任何一种情况下,由于您不太可能事先知道.then
处理程序需要多少个正式参数,该处理程序需要处理arguments
数组才能检索结果每一个承诺。
答案 1 :(得分:102)
上面的解决方法(谢谢!)没有正确解决返回提供给延迟的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)]);
});
});
}
}
现在你可以简单地传入一个deferreds / promises数组,并在你的回调函数中找回一系列已解析/被拒绝的对象,如下所示:
$.when.all(deferreds).then(function(objects) {
console.log("Resolved objects:", objects);
});
答案 2 :(得分:38)
您可以将when
方法应用于数组:
var arr = [ /* Deferred objects */ ];
$.when.apply($, arr);
答案 3 :(得分:7)
当调用多个并行AJAX调用时,您有两个选项来处理相应的响应。
Promises'
的{{1}}数组和$.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()
})
答案 4 :(得分:5)
作为一个简单的替代方案,不需要$.when.apply
或array
,您可以使用以下模式为多个并行承诺生成单个承诺:
promise = $.when(promise, anotherPromise);
e.g。
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>");
});
});
});
promise = promise.then(newpromise)
答案 5 :(得分:4)
我想提出另一个使用$ .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!");
}
)
答案 6 :(得分:1)
如果您正在进行转换并且可以访问ES6,则可以使用扩展语法,该语法将对象的每个可迭代项目专门应用为离散参数,就像$.when()
需要它一样。
$.when(...deferreds).done(() => {
// do stuff
});
答案 7 :(得分:0)
如果您正在使用angularJS或Q promise库的某些变体,那么您可以使用.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
答案 8 :(得分:0)
我有一个非常相似的案例,我在每个循环中发布,然后在某些字段中设置html标记,从ajax收到的数字。然后,我需要对这些字段的(现在更新的)值进行求和并放在总字段中。
因此问题是我试图对所有数字进行总结,但是没有数据从异步ajax调用中返回。我需要在一些函数中完成此功能才能重用代码。我之前的外部函数等待数据,然后再用完全更新的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;
}