使用jQuery的并行asynchronousAjax请求

我想根据多个Ajax / JSON请求的结果更新一个页面。 使用jQuery,我可以“链接”callback,就像这个非常简单的剥离示例:

$.getJSON("/values/1", function(data) { // data = {value: 1} var value_1 = data.value; $.getJSON("/values/2", function(data) { // data = {value: 42} var value_2 = data.value; var sum = value_1 + value_2; $('#mynode').html(sum); }); }); 

但是,这会导致请求被连续进行。 我宁愿一种方式来并行请求,完成后执行页面更新。 有没有办法做到这一点?

试试这个解决scheme,它可以支持任何特定数量的并行查询:

 var done = 4; // number of total requests var sum = 0; /* Normal loops don't create a new scope */ $([1,2,3,4,5]).each(function() { var number = this; $.getJSON("/values/" + number, function(data) { sum += data.value; done -= 1; if(done == 0) $("#mynode").html(sum); }); }); 

jQuery $ .when()和$ .done()正是你所需要的:

 $.when($.ajax("/page1.php"), $.ajax("/page2.php")) .then(myFunc, myFailure); 

这是我直接解决你的问题的尝试

基本上,你只需build立起AJAX调用堆栈,执行所有事件,并在所有事件完成时调用提供的函数 – 提供的参数是所有提供的ajax请求的结果数组。

很明显,这是早期的代码 – 在灵活性方面,您可以更加详细地阐述这一点。

 <script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"></script> <script type="text/javascript"> var ParallelAjaxExecuter = function( onComplete ) { this.requests = []; this.results = []; this.onComplete = onComplete; } ParallelAjaxExecuter.prototype.addRequest = function( method, url, data, format ) { this.requests.push( { "method" : method , "url" : url , "data" : data , "format" : format , "completed" : false } ) } ParallelAjaxExecuter.prototype.dispatchAll = function() { var self = this; $.each( self.requests, function( i, request ) { request.method( request.url, request.data, function( r ) { return function( data ) { console.log r.completed = true; self.results.push( data ); self.checkAndComplete(); } }( request ) ) } ) } ParallelAjaxExecuter.prototype.allRequestsCompleted = function() { var i = 0; while ( request = this.requests[i++] ) { if ( request.completed === false ) { return false; } } return true; }, ParallelAjaxExecuter.prototype.checkAndComplete = function() { if ( this.allRequestsCompleted() ) { this.onComplete( this.results ); } } var pe = new ParallelAjaxExecuter( function( results ) { alert( eval( results.join( '+' ) ) ); } ); pe.addRequest( $.get, 'test.php', {n:1}, 'text' ); pe.addRequest( $.get, 'test.php', {n:2}, 'text' ); pe.addRequest( $.get, 'test.php', {n:3}, 'text' ); pe.addRequest( $.get, 'test.php', {n:4}, 'text' ); pe.dispatchAll(); </script> 

这里是test.php

 <?php echo pow( $_GET['n'], 2 ); ?> 

更新:根据Yair Leviel给出的答案,这个答案已经过时了。 使用承诺库,如jQuery.when()或Q.js.


我创build了一个通用的解决scheme作为jQuery扩展。 可以使用一些微调来使其更一般,但它适合我的需求。 截至撰写本文时,此技术相对于其他技术的优点是可以使用任何types的具有callback的asynchronous处理。

注意:如果我认为我的客户端可以依赖另一个第三方库,那么我会使用JavaScript的Rx扩展,而不是这个。

 // jQuery extension for running multiple async methods in parallel // and getting a callback with all results when all of them have completed. // // Each worker is a function that takes a callback as its only argument, and // fires up an async process that calls this callback with its result. // // Example: // $.parallel( // function (callback) { $.get("form.htm", {}, callback, "html"); }, // function (callback) { $.post("data.aspx", {}, callback, "json"); }, // function (formHtml, dataJson) { // // Handle success; each argument to this function is // // the result of correlating ajax call above. // } // ); (function ($) { $.parallel = function (anyNumberOfWorkers, allDoneCallback) { var workers = []; var workersCompleteCallback = null; // To support any number of workers, use "arguments" variable to // access function arguments rather than the names above. var lastArgIndex = arguments.length - 1; $.each(arguments, function (index) { if (index == lastArgIndex) { workersCompleteCallback = this; } else { workers.push({ fn: this, done: false, result: null }); } }); // Short circuit this edge case if (workers.length == 0) { workersCompleteCallback(); return; } // Fire off each worker process, asking it to report back to onWorkerDone. $.each(workers, function (workerIndex) { var worker = this; var callback = function () { onWorkerDone(worker, arguments); }; worker.fn(callback); }); // Store results and update status as each item completes. // The [0] on workerResultS below assumes the client only needs the first parameter // passed into the return callback. This simplifies the handling in allDoneCallback, // but may need to be removed if you need access to all parameters of the result. // For example, $.post calls back with success(data, textStatus, XMLHttpRequest). If // you need textStatus or XMLHttpRequest then pull off the [0] below. function onWorkerDone(worker, workerResult) { worker.done = true; worker.result = workerResult[0]; // this is the [0] ref'd above. var allResults = []; for (var i = 0; i < workers.length; i++) { if (!workers[i].done) return; else allResults.push(workers[i].result); } workersCompleteCallback.apply(this, allResults); } }; })(jQuery); 

更新另外两年后,这看起来很疯狂,因为接受的答案已经变得更好了! (尽pipe还不如Yair Leviel使用jQuery的答案)

18个月后,我刚刚碰到类似的东西。 我有一个刷新button,我想老的内容fadeOut ,然后新的内容fadeIn 。 但是我也需要get新的内容。 fadeOutget是asynchronous的,但是串行运行会浪费时间。

我所做的和被接受的答案是一样的,除了可重用函数的forms。 它的主要优点是比这里的其他build议短得多。

 var parallel = function(actions, finished) { finishedCount = 0; var results = []; $.each(actions, function(i, action) { action(function(result) { results[i] = result; finishedCount++; if (finishedCount == actions.length) { finished(results); } }); }); }; 

你传递一个函数的数组并行运行。 每个函数都应该接受另一个函数来传递它的结果(如果有的话)。 parallel将提供该function。

当所有的操作完成时,你也传递一个函数来调用。 这将收到一个数组,所有的结果。所以我的例子是:

 refreshButton.click(function() { parallel([ function(f) { contentDiv.fadeOut(f); }, function(f) { portlet.content(f); }, ], function(results) { contentDiv.children().remove(); contentDiv.append(results[1]); contentDiv.fadeIn(); }); }); 

所以当我的刷新button被点击时,我启动了jQuery的fadeOut效果,同时也启动了我自己的portlet.content函数(它执行asynchronousget ,创build一个新的内容并传递它),然后当两者都完成时,内容,追加第二个函数的结果(在results[1] ),并fadeIn新的内容。

由于fadeOut不会传递任何东西给它的完成函数, results[0]可能包含undefined ,所以我忽略它。 但是,如果你有三个有用的结果的操作,他们会插入results数组,按照你传递函数的顺序。

你可以做这样的事情

 var allData = [] $.getJSON("/values/1", function(data) { allData.push(data); if(data.length == 2){ processData(allData) // where process data processes all the data } }); $.getJSON("/values/2", function(data) { allData.push(data); if(data.length == 2){ processData(allData) // where process data processes all the data } }); var processData = function(data){ var sum = data[0] + data[1] $('#mynode').html(sum); } 

并行运行多个AJAX请求

在使用API​​时,您有时需要向不同的端点发出多个AJAX请求。 在发布下一个请求之前,您可以通过使用jQuery的$.when()函数并行请求数据来加快jQuery的速度:

JS

 $.when($.get('1.json'), $.get('2.json')).then(function(r1, r2){ console.log(r1[0].message + " " + r2[0].message); }); 

当这两个GET请求都成功完成时,将执行callback函数。 $ .when()接受两个$ .get()调用返回的promise,并构造一个新的promise对象。 callback的r1和r2参数是数组,其第一个元素包含服务器响应。

这是一个使用mbostock / queue的实现:

 queue() .defer(function(callback) { $.post('/echo/json/', {json: JSON.stringify({value: 1}), delay: 1}, function(data) { callback(null, data.value); }); }) .defer(function(callback) { $.post('/echo/json/', {json: JSON.stringify({value: 3}), delay: 2}, function(data) { callback(null, data.value); }); }) .awaitAll(function(err, results) { var result = results.reduce(function(acc, value) { return acc + value; }, 0); console.log(result); }); 

相关的小提琴: http : //jsfiddle.net/MdbW2/

通过以下JQuery扩展(可以将其作为独立函数编写,您可以这样做:

 $.whenAll({ val1: $.getJSON('/values/1'), val2: $.getJSON('/values/2') }) .done(function (results) { var sum = results.val1.value + results.val2.value; $('#mynode').html(sum); }); 

当所有()的JQuery(1.x)扩展:

 $.whenAll = function (deferreds) { function isPromise(fn) { return fn && typeof fn.then === 'function' && String($.Deferred().then) === String(fn.then); } var d = $.Deferred(), keys = Object.keys(deferreds), args = keys.map(function (k) { return $.Deferred(function (d) { var fn = deferreds[k]; (isPromise(fn) ? fn : $.Deferred(fn)) .done(d.resolve) .fail(function (err) { d.reject(err, k); }) ; }); }); $.when.apply(this, args) .done(function () { var resObj = {}, resArgs = Array.prototype.slice.call(arguments); resArgs.forEach(function (v, i) { resObj[keys[i]] = v; }); d.resolve(resObj); }) .fail(d.reject); return d; }; 

请参阅jsbin示例: http ://jsbin.com/nuxuciwabu/edit?js,console

对我来说最专业的解决scheme是使用async.js和Array.reduce,如下所示:

  async.map([1, 2, 3, 4, 5], function (number, callback) { $.getJSON("/values/" + number, function (data) { callback(null, data.value); }); }, function (err, results) { $("#mynode").html(results.reduce(function(previousValue, currentValue) { return previousValue + currentValue; })); }); 

如果一个请求的结果取决于另一个请求,则不能使它们平行。

build立在Yair的答案。 你可以dynamic地定义ajax promise。

 var start = 1; // starting value var len = 2; // no. of requests var promises = (new Array(len)).fill().map(function() { return $.ajax("/values/" + i++); }); $.when.apply($, promises) .then(myFunc, myFailure); 
 say suppose you have a array of file name. var templateNameArray=["test.html","test2.html","test3.html"]; htmlTemplatesLoadStateMap={}; var deffereds=[]; for (var i = 0; i < templateNameArray.length; i++) { if (!htmlTemplatesLoadStateMap[templateNameArray[i]]) { deferreds.push($.get("./Content/templates/" +templateNameArray[i], function (response, status, xhr) { if (status == "error") { } else { $("body").append(response); } })); htmlTemplatesLoadStateMap[templateNameArray[i]] = true; } } $.when.all(deferreds).always(function(resultsArray) { yourfunctionTobeExecuted(yourPayload); }); Hope this helps...!!