将一个Deferreds数组传递给$ .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()似乎不知道如何处理延迟对象数组。 “全做完了!” 首先发生,因为该数组不是一个Deferred对象,所以jQuery继续前进,并假设它刚刚完成。

我知道可以将对象传递给像$.when(deferred1, deferred2, ..., deferredX)的函数$.when(deferred1, deferred2, ..., deferredX)但未知有多lessDeferred对象会在我试图解决的实际问题中执行。

要传递一个值的数组到任何通常期望他们是单独的参数,使用Function.prototype.apply ,所以在这种情况下,你需要:

 $.when.apply($, my_array).then( ___ ); 

http://jsfiddle.net/YNGcm/21/

在ES6中,您可以使用... spread操作符代替:

 $.when(...my_array).then( ___ ); 

在任何一种情况下,由于不太可能事先知道.then处理程序需要的正式参数,该处理程序需要处理arguments数组以便检索每个承诺的结果。

上面的解决方法(谢谢!)没有正确地解决回收提供给延迟的resolve()方法的对象的问题,因为jQuery调用具有单个参数的done()fail()callback而不是数组。 这意味着我们必须使用arguments pseudo-array来获取由延迟数组返回的所有parsing/拒绝对象,这很丑陋:

 $.when.apply($,deferreds).then(function() { var objects=arguments; // The array of resolved objects as a pseudo-array ... }; 

由于我们传入了一个延迟数组,所以返回结果数组将会很好。 取回一个实际的数组而不是一个伪数组也是很好的,所以我们可以使用像Array.sort()这样的方法。

这个解决scheme是由when.jswhen.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 / promise数组,并在您的callback中取回已parsing/已拒绝的对象数组,如下所示:

 $.when.all(deferreds).then(function(objects) { console.log("Resolved objects:", objects); }); 

你可以将when方法应用到你的数组中:

 var arr = [ /* Deferred objects */ ]; $.when.apply($, arr); 

你如何使用jQuery Deferreds的数组?

作为一个简单的select,它不需要$.when.apply或一个array ,您可以使用以下模式为多个并行promise生成单个promise:

 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)
  • 缺点是它在后台创build额外的promise对象,并且最后传递的任何参数都不是很有用(因为它们嵌套在附加对象内)。 为了你想要的,虽然它简短。
  • 好处是它不需要arrays或arrayspipe理。

当调用多个并行的AJAX调用时,您有两个选项来处理相应的响应。

  1. 使用同步AJAX调用/一个接一个/不推荐
  2. 使用Promises'数组和$.when接受promise和它的callback.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> 

我想使用$ .each提出另一个:

  1. 我们可能会声明一个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; } } 
  2. 我们用ajax创build函数数组的代码部分:

     var arrayOfFn = []; for (var i = 0; i < someDataArray.length; i++) { var ajaxFnForArray = new ajaxFn(someDataArray[i]); arrayOfFn.push(ajaxFnForArray); } 
  3. 并通过发送ajax调用函数:

     $.when( $.each(arrayOfFn, function(index, value) { value.call() }) ).then(function() { alert("Cheer!"); } ) 

如果你正在编译和访问ES6,你可以使用扩展的语法来明确地应用每个对象的迭代项作为离散的参数,就像$.when()需要的那样。

 $.when(...deferreds).done(() => { // do stuff }); 

MDN链接 – 扩展语法

我有一个非常相似的情况,我在每个循环中发布,然后在从ajax收到的数字中设置一些字段的html标记。 然后,我需要对这些字段的(现在更新的)值进行总和,并将其放在总字段中。

因此,问题是我试图对所有数字进行求和,但没有数据从asynchronousajax调用返回。 我需要在几个函数中完成这个function才能够重用代码。 我的外部函数等待数据,然后我去做一些东西与完全更新的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; } 

如果你正在使用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

Interesting Posts