Richieste Ajax asincrone parallele che utilizzano jQuery

Vorrei aggiornare una pagina in base ai risultati di più richieste ajax / json. Usando jQuery, posso “concatenare” i callback, come questo esempio molto semplice e spoglio:

$.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); }); }); 

Tuttavia, questo comporta che le richieste vengano fatte in serie. Preferirei piuttosto un modo per rendere le richieste in parallelo ed eseguire l’aggiornamento della pagina dopo che tutti sono stati completati. C’è un modo per fare questo?

Prova questa soluzione, in grado di supportare qualsiasi numero specifico di query parallele:

 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 () e $ .done () sono esattamente ciò di cui hai bisogno:

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

Aggiornamento: Per la risposta data da Yair Leviel, questa risposta è obsoleta. Utilizza una libreria di promise, come jQuery.when () o Q.js.


Ho creato una soluzione generica come estensione jQuery. Potrei usare un po ‘di messa a punto per renderlo più generale, ma è adatto alle mie esigenze. Il vantaggio di questa tecnica rispetto agli altri in questa pubblicazione al momento della stesura di questo articolo è che è ansible utilizzare qualsiasi tipo di elaborazione asincrona con una callback.

Nota: userei le estensioni Rx per JavaScript invece di questo se pensassi che il mio cliente sarebbe d’accordo nel prendere una dipendenza da ancora-un’altra-libreria di terze parti 🙂

 // 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); 

Ecco il mio tentativo di affrontare direttamente la tua domanda

Fondamentalmente, basta creare stack di chiamate AJAX, eseguirli tutti e una funzione fornita viene chiamata al completamento di tutti gli eventi – l’argomento fornito è una matrice dei risultati di tutte le richieste Ajax fornite.

Chiaramente questo è il codice precoce: potresti essere più elaborato in termini di flessibilità.

   

ecco test.php

  

AGGIORNAMENTO E altri due anni dopo, questo sembra assurdo perché la risposta accettata è cambiata in qualcosa di molto meglio! (Anche se non è ancora buono come la risposta di Yair Leviel usando jQuery when )

18 mesi dopo, ho appena colpito qualcosa di simile. Ho un pulsante di aggiornamento, e voglio il vecchio contenuto di fadeOut e poi il nuovo contenuto di fadeIn . Ma ho anche bisogno di get il nuovo contenuto. Il fadeOut e il get sono asincroni, ma sarebbe una perdita di tempo eseguirli in serie.

Quello che faccio è lo stesso della risposta accettata, tranne che sotto forma di una funzione riutilizzabile. La sua virtù principale è che è molto più breve rispetto agli altri suggerimenti qui.

 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); } }); }); }; 

Si passa a una serie di funzioni da eseguire in parallelo. Ogni funzione dovrebbe accettare un’altra funzione a cui passa il suo risultato (se presente). parallel fornirà quella funzione.

Passa anche una funzione da chiamare quando tutte le operazioni sono state completate. Questo riceverà un array con tutti i risultati. Quindi il mio esempio è stato:

 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(); }); }); 

Quindi, quando si fa clic sul mio pulsante di aggiornamento, avvio l’effetto fadeOut di jQuery e anche la mia funzione portlet.content (che esegue un async, crea un nuovo bit di contenuto e lo passa), quindi quando entrambi sono completi rimuovo il vecchio contenuto, aggiungi il risultato della seconda funzione (che è nei results[1] ) e fadeIn il nuovo contenuto.

Siccome fadeOut non passa nulla alla sua funzione di completamento, i results[0] presumibilmente contengono undefined , quindi lo ignoro. Ma se avessi tre operazioni con risultati utili, avrebbero inserito ciascuna fessura nell’array dei results , nello stesso ordine in cui hai passato le funzioni.

Eseguire più richieste AJAX in parallelo

Quando si lavora con le API, a volte è necessario inviare più richieste AJAX a diversi endpoint. Invece di aspettare che una richiesta venga completata prima di emettere la successiva, puoi accelerare le cose con jQuery richiedendo i dati in parallelo, usando la funzione $.when() jQuery:

JS

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

La funzione di callback viene eseguita quando entrambe queste richieste GET terminano correttamente. $ .when () prende le promesse restituite da due chiamate $ .get () e costruisce un nuovo object promise. Gli argomenti r1 e r2 del callback sono array, i cui primi elementi contengono le risposte del server.

potresti fare qualcosa di simile

 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); } 

Ecco un’implementazione usando 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); }); 

Il violino associato: http://jsfiddle.net/MdbW2/

Con la seguente estensione di JQuery (che può essere scritta come una funzione indipendente puoi farlo:

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

L’estensione JQuery (1.x) whenAll ():

 $.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; }; 

Vedi l’esempio di jsbin: http://jsbin.com/nuxuciwabu/edit?js,console

La soluzione più professionale per me sarebbe utilizzare async.js e Array.reduce in questo modo:

  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; })); }); 

Se il risultato di una richiesta dipende dall’altra, non è ansible renderle parallele.

Basandosi sulla risposta di Yair. È ansible definire le promesse di Ajax in modo dinamico.

 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...!!