提问人:adamjford 提问时间:4/12/2011 最后编辑:Alnitakadamjford 更新时间:5/5/2021 访问量:132837
将 Deferreds 数组传递给 $.when()
Pass in an array of Deferreds to $.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>");
});
});
});
我希望在所有延迟任务完成后出现“全部完成!”,但似乎不知道如何处理延迟对象的数组。“All done!”首先发生,因为数组不是 Deferred 对象,因此 jQuery 继续并假设它刚刚完成。$.when()
我知道可以将对象传递到函数中,但不知道在我试图解决的实际问题中,执行时会有多少个延迟对象。$.when(deferred1, deferred2, ..., deferredX)
答:
要将值数组传递给通常期望它们为单独参数的任何函数,请使用 ,因此在本例中需要:Function.prototype.apply
$.when.apply($, my_array).then( ___ );
查看 http://jsfiddle.net/YNGcm/21/
在 ES6 中,您可以改用 spread 运算符:...
$.when(...my_array).then( ___ );
在任何一种情况下,由于您不太可能事先知道处理程序需要多少个形式参数,因此该处理程序需要处理数组才能检索每个承诺的结果。.then
arguments
评论
$.when
f.apply(ctx, my_array)
f
this == ctx
my_array
$
null
$
null
$.when
this
您可以将该方法应用于您的数组:when
var arr = [ /* Deferred objects */ ];
$.when.apply($, arr);
评论
上面的解决方法(谢谢!)没有正确解决取回提供给延迟方法的对象的问题,因为 jQuery 使用单个参数而不是数组调用 and 回调。这意味着我们必须使用伪数组来获取延迟数组返回的所有已解析/拒绝的对象,这很丑陋:resolve()
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(
// the calling function will receive an array of length N, where N is the number of
// deferred objects passed to when.all that succeeded. each element in that array will
// itself be an array of 3 objects, corresponding to the arguments passed to jqXHR.done:
// ( data, textStatus, jqXHR )
function () {
var arrayThis, arrayArguments;
if (Array.isArray(this)) {
arrayThis = this;
arrayArguments = arguments;
}
else {
arrayThis = [this];
arrayArguments = [arguments];
}
def.resolveWith(arrayThis, [Array.prototype.slice.call(arrayArguments)]);
},
// the calling function will receive an array of length N, where N is the number of
// deferred objects passed to when.all that failed. each element in that array will
// itself be an array of 3 objects, corresponding to the arguments passed to jqXHR.fail:
// ( jqXHR, textStatus, errorThrown )
function () {
var arrayThis, arrayArguments;
if (Array.isArray(this)) {
arrayThis = this;
arrayArguments = arguments;
}
else {
arrayThis = [this];
arrayArguments = [arguments];
}
def.rejectWith(arrayThis, [Array.prototype.slice.call(arrayArguments)]);
});
});
}
}
现在,您可以简单地传入一个延迟/承诺数组,并在回调中取回一个已解析/拒绝的对象数组,如下所示:
$.when.all(deferreds).then(function(objects) {
console.log("Resolved objects:", objects);
});
评论
var toArray = function (args) { return deferreds.length > 1 ? $.makeArray(args) : [args]; }
Array.prototype.slice.call
如果你使用的是 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
https://docs.angularjs.org/api/ng/service/$q
评论
.map
作为一个简单的替代方法,即不需要 或 ,您可以使用以下模式为多个并行 promise 生成单个 promise:$.when.apply
array
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>");
});
});
});
笔记:
- 在看到有人按顺序链接承诺后,我想通了这一点,使用
promise = promise.then(newpromise)
- 缺点是它会在幕后创建额外的 promise 对象,并且最后传递的任何参数都不是很有用(因为它们嵌套在其他对象中)。对于你想要的,虽然它简短而简单。
- 优点是它不需要阵列或阵列管理。
评论
then()
$.when
调用多个并行 AJAX 调用时,有两个选项可用于处理相应的响应。
- 使用同步 AJAX 调用/一个接一个/不推荐
- 使用数组和
$.when
,当所有 s 都成功返回并返回相应的响应时,它会调用 s 和它的回调。Promises'
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's Native Names : </h4> <span id="countries"></span>
</div>
评论
$.when
for ... in
(not recommended)
for ... in
Array.prototype
.map
for
push
var promises = capitalCities.map(ajaxRequest); $.when.apply($, promises).then(fillCountryCapitals)
我想用$.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!"); } )
如果你正在转译并可以访问 ES6,你可以使用扩展语法,它专门将对象的每个可迭代项作为离散参数应用,就像需要它一样。$.when()
$.when(...deferreds).done(() => {
// do stuff
});
我有一个非常相似的情况,我在每个循环中发布,然后从从 ajax 收到的数字在某些字段中设置 html 标记。然后,我需要对这些字段的(现已更新的)值进行求和,并将其放入总字段中。
因此,问题在于我试图对所有数字进行求和,但尚未从异步 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;
}
上一个:将元组扩展为参数
评论
$.when.apply