Równoległe asynchroniczne żądania Ajax przy użyciu jQuery

Chciałbym zaktualizować stronę na podstawie wyników wielu żądań ajax/json. Używając jQuery, mogę "połączyć" wywołania zwrotne, jak w tym bardzo prostym przykładzie:

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

});

Powoduje to jednak, że wnioski są składane seryjnie. Wolałbym raczej sposób na równoległe składanie wniosków i wykonywanie aktualizacji strony po zakończeniu wszystkich. Jest na to jakiś sposób?

Author: Chris Pietschmann, 2009-06-30

13 answers

Wypróbuj To rozwiązanie, które może obsługiwać dowolną liczbę równoległych zapytań:

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);
  });
});
 97
Author: Yehuda Katz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 21:10:53

JQuery $.when () i $.done () są dokładnie tym, czego potrzebujesz:

$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
  .then(myFunc, myFailure);
 106
Author: Yair Leviel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-08 16:47:36

Update: zgodnie z odpowiedzią udzieloną przez Yaira Leviela, ta odpowiedź jest przestarzała. Użyj biblioteki promise, takiej jak jQuery.when() lub Q. js.


Stworzyłem rozwiązanie ogólnego przeznaczenia jako rozszerzenie jQuery. Przydałoby się trochę dostrojenia, żeby było bardziej ogólne, ale odpowiadało moim potrzebom. Zaletą tej techniki nad innymi w tym wpisie od czasu tego pisania było to, że dowolny rodzaj asynchronicznego przetwarzania z wywołaniem zwrotnym może być używany.

Uwaga: użyłbym RX extensions for JavaScript zamiast tego, jeśli myślałem, że mój klient będzie w porządku z biorąc zależność od yet-another-third-party-library:)

// 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);
 9
Author: pettys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-28 14:07:04

Oto moja próba bezpośredniej odpowiedzi na twoje pytanie.]}

Zasadniczo, po prostu budujesz stos wywołań AJAX, wykonujesz je wszystkie, a dostarczona funkcja jest wywoływana po zakończeniu wszystkich zdarzeń - podany argument jest tablicą wyników wszystkich dostarczonych żądań ajax.

Oczywiście jest to wczesny Kod - możesz uzyskać bardziej rozbudowany pod względem elastyczności.

<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>
Oto test.php
<?php

echo pow( $_GET['n'], 2 );

?>
 8
Author: Peter Bailey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 21:58:41

UPDATE i kolejne dwa lata później wygląda to szalenie, ponieważ przyjęta odpowiedź zmieniła się na coś znacznie lepszego! (Choć nadal nie tak dobra jak odpowiedź Yaira Leviela przy użyciu jQuery ' S when)

18 miesięcy później Uderzyłem w coś podobnego. Mam przycisk odświeżania i chcę, aby stara zawartość była fadeOut, a nowa zawartość fadeIn. Ale potrzebuję też get nowej treści. fadeOut i get są asynchroniczne, ale ich uruchamianie byłoby stratą czasu seryjnie.

To, co robię, jest tak naprawdę takie samo, jak zaakceptowana odpowiedź, z wyjątkiem funkcji wielokrotnego użytku. Jego główną zaletą jest to, że jest znacznie krótszy niż inne sugestie tutaj.

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

Przekazujesz mu szereg funkcji, które będą działać równolegle. Każda funkcja powinna przyjąć inną funkcję, do której przekazuje swój wynik(jeśli istnieje). parallel dostarczy tę funkcję.

Przekazujesz mu również funkcję, która zostanie wywołana po zakończeniu wszystkich operacji. To będzie otrzymujemy tablicę ze wszystkimi wynikami. Tak więc mój przykład brzmiał:

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

Więc kiedy mój przycisk odświeżania jest kliknięty, uruchamiam efekt fadeOut jQuery, a także moją własną funkcję portlet.content (która wykonuje asynchronizację get, buduje nowy bit zawartości i przekazuje go dalej), a następnie gdy oba są kompletne, usuwam starą zawartość, dołączam wynik drugiej funkcji (która jest w results[1]) i fadeIn nowej zawartości.

Ponieważ fadeOut nie przekazuje niczego do swojej funkcji dopełnienia, results[0] przypuszczalnie zawiera undefined, więc go ignoruję. Ale gdybyś miał trzy operacje z użytecznymi wynikami, każda z nich byłaby szczeliną w tablicy results, w tej samej kolejności, w jakiej przekazywałeś funkcje.

 7
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-03 09:10:21

Uruchamianie wielu żądań AJAX równolegle

Podczas pracy z interfejsami API czasami trzeba wysyłać wiele żądań AJAX do różnych punktów końcowych. Zamiast czekać na zakończenie jednego żądania przed wydaniem następnego, możesz przyspieszyć działanie jQuery, żądając danych równolegle, używając funkcji $.when() jQuery:

JS

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

Funkcja wywołania zwrotnego jest wykonywana, gdy oba te żądania GET zakończą się pomyślnie. $.kiedy () bierze obietnice zwrócone o dwa dolary.get() wywołuje i konstruuje nowy obiekt promise. Argumenty R1 i r2 wywołania zwrotnego są tablicami, których pierwsze elementy zawierają odpowiedzi serwera.

 6
Author: sri_bb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-11 06:24:13

Mógłbyś zrobić coś takiego

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);
}
 5
Author: agilefall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 21:12:26

Oto implementacja używająca 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);
  });

The associated fiddle: http://jsfiddle.net/MdbW2/

 3
Author: Giovanni Cappellotto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-28 13:28:31

Z następującym rozszerzeniem JQuery (to może być napisane jako samodzielna funkcja możesz to zrobić:

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

Zobacz przykład jsbin: http://jsbin.com/nuxuciwabu/edit?js, console

 3
Author: mraxus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-26 00:20:30

Najbardziej profesjonalnym rozwiązaniem dla mnie byłoby użycie asynchronicznego.js i Array.zmniejsz tak:

        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;
            }));
        });
 3
Author: George Mavritsakis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-04 18:26:24

Jeśli wynik jednego żądania zależy od drugiego, nie można ustawić ich równolegle.

 1
Author: Luca Matteis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 21:06:33

Bazując na odpowiedzi Yaira. Możesz definiować obietnice ajax dynamicznie.

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);
 1
Author: Gabiriele Lalasava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-21 15:18:28

Załóżmy, że masz tablicę nazw plików.

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);
                                });
 0
Author: Prashant Saurabh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-05 08:16:23