it-swarm.com.de

jQuery Callback für mehrere Ajax-Aufrufe

Ich möchte drei Ajax-Aufrufe in einem Click-Ereignis durchführen. Jeder Ajax-Aufruf führt eine eindeutige Operation aus und gibt Daten zurück, die für einen abschließenden Rückruf erforderlich sind. Die Anrufe selbst sind nicht voneinander abhängig, sie können alle gleichzeitig ablaufen, aber ich hätte gerne einen letzten Rückruf, wenn alle drei abgeschlossen sind.

$('#button').click(function() {
    fun1();
    fun2();
    fun3();
//now do something else when the requests have done their 'success' callbacks.
});

var fun1= (function() {
    $.ajax({/*code*/});
});
var fun2 = (function() {
    $.ajax({/*code*/});
});
var fun3 = (function() {
    $.ajax({/*code*/});
});
116
MisterIsaak

Hier ist ein Callback-Objekt, das ich geschrieben habe, in dem Sie entweder einen einzelnen Callback so einstellen können, dass er ausgelöst wird, wenn alle abgeschlossen sind, oder dass jeder einen eigenen Callback hat und alle ausgelöst wird, wenn alle beendet sind:

BEACHTEN

Seit jQuery 1.5+ können Sie die zurückgestellte Methode wie in einer anderen Antwort beschrieben verwenden:

  $.when($.ajax(), [...]).then(function(results){},[...]);

Beispiel für hier verschoben

für jQuery <1.5 wird Folgendes funktionieren oder wenn Sie möchten, dass Ihre Ajax-Anrufe zu unbekannten Zeiten ausgelöst werden, wie hier mit zwei Schaltflächen gezeigt: Wird ausgelöst, nachdem beide Schaltflächen angeklickt wurden

[Verwendungszweck]

für single callback einmal abgeschlossen: Arbeitsbeispiel

// initialize here
var requestCallback = new MyRequestsCompleted({
    numRequest: 3,
    singleCallback: function(){
        alert( "I'm the callback");
    }
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});

jeder hat seinen eigenen Callback, wenn alle abgeschlossen sind: Arbeitsbeispiel

//initialize 
var requestCallback = new MyRequestsCompleted({
    numRequest: 3
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the first callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the second callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the third callback');
        });
    }
});

[Der Code]

var MyRequestsCompleted = (function() {
    var numRequestToComplete, requestsCompleted, callBacks, singleCallBack;

    return function(options) {
        if (!options) options = {};

        numRequestToComplete = options.numRequest || 0;
        requestsCompleted = options.requestsCompleted || 0;
        callBacks = [];
        var fireCallbacks = function() {
            alert("we're all complete");
            for (var i = 0; i < callBacks.length; i++) callBacks[i]();
        };
        if (options.singleCallback) callBacks.Push(options.singleCallback);

        this.addCallbackToQueue = function(isComplete, callback) {
            if (isComplete) requestsCompleted++;
            if (callback) callBacks.Push(callback);
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.requestComplete = function(isComplete) {
            if (isComplete) requestsCompleted++;
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.setCallback = function(callback) {
            callBacks.Push(callBack);
        };
    };
})();
97
subhaze

Sieht so aus, als hätten Sie einige Antworten darauf, aber ich denke, es gibt hier etwas Erwähnenswertes, das Ihren Code stark vereinfacht. In jQuery wurde der $.when in v1.5 eingeführt. Es sieht aus wie:

$.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) {
    //this callback will be fired once all ajax calls have finished.
});

Ich habe es hier nicht erwähnt, ich hoffe es hilft.

139
Greg

Ich habe selbst nicht die Notwendigkeit eines Objektes gesehen. Simple hat eine Variable, die eine ganze Zahl ist. Wenn Sie eine Anfrage starten, erhöhen Sie die Nummer. Wenn man fertig ist, dekrementiere es. Wenn es null ist, sind keine Anforderungen in Bearbeitung, also sind Sie fertig.

$('#button').click(function() {
    var inProgress = 0;

    function handleBefore() {
        inProgress++;
    };

    function handleComplete() {
        if (!--inProgress) {
            // do what's in here when all requests have completed.
        }
    };

    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
});
9
Matt

Da $.when alle ajax-Anforderungen als sequentielle Argumente (kein Array) erwartet, wird $.when normalerweise mit .apply() verwendet.

// Save all requests in an array of jqXHR objects
var requests = arrayOfThings.map(function(thing) {
    return $.ajax({
        method: 'GET',
        url: 'thing/' + thing.id
    });
});
$.when.apply(this, requests).then(function(resp1, resp2/*, ... */) {
    // Each argument is an array with the following structure: [ data, statusText, jqXHR ]
    var responseArgsArray = Array.prototype.slice.call(this, arguments);

});

Dies liegt daran, dass $.when solche Argumente akzeptiert

$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);

Und nicht so:

$.when([ajaxRequest1, ajaxRequest2, ajaxRequest3]);
8
Cory Danielson

Ich mag die Idee von hvgotcodes. Mein Vorschlag ist, einen generischen Inkrementierer hinzuzufügen, der die vollständige Zahl mit der benötigten Anzahl vergleicht und dann den endgültigen Rückruf ausführt. Dies könnte in den letzten Rückruf integriert werden.

var sync = {
 callbacksToComplete = 3,
 callbacksCompleted = 0,
 addCallbackInstance = function(){
  this.callbacksCompleted++;
  if(callbacksCompleted == callbacksToComplete) {
   doFinalCallBack();
  }
 }
};

[Bearbeitet, um Namensaktualisierungen anzuzeigen.]

4
Adolph Trudeau

BEARBEITEN - vielleicht ist es die beste Option, einen Service-Endpunkt zu erstellen, der alle drei Anforderungen erfüllt. Auf diese Weise müssen Sie nur eine Anfrage ausführen, und alle Daten sind dort, wo Sie sie benötigen, um in der Antwort zu sein. Wenn Sie feststellen, dass Sie immer und immer wieder die gleichen drei Anforderungen ausführen, möchten Sie wahrscheinlich diese Route wählen. Es ist oft eine gute Entwurfsentscheidung, einen Fassadendienst auf dem Server einzurichten, der üblicherweise kleinere Serveraktionen zusammenfasst. Nur eine Idee.


eine Möglichkeit wäre, ein 'Sync'-Objekt in Ihrem Click-Handler zu erstellen, bevor die Ajax-Aufrufe erfolgen. So etwas wie

var sync = {
   count: 0
}

Die Synchronisierung wird automatisch an den Umfang der Erfolgsaufrufe gebunden (Schließung). Im Erfolgs-Handler erhöhen Sie die Anzahl und wenn es 3 ist, können Sie die andere Funktion aufrufen.

Alternativ können Sie so etwas tun

var sync = {
   success1Complete: false,
   ...
   success3Complete: false,
}

wenn jeder Erfolg ausgeführt wird, wird der Wert in der Synchronisierung in true geändert. Sie müssen die Synchronisierung überprüfen, um sicherzustellen, dass alle drei zutreffen, bevor Sie fortfahren.

Beachten Sie den Fall, dass eine Ihrer Xhrs keinen Erfolg erzielt - Sie müssen dies berücksichtigen.

Eine weitere Option wäre, die letzte Funktion immer in Ihren Erfolgskontrollern aufzurufen und auf die Synchronisierungsoption zuzugreifen, um zu bestimmen, ob tatsächlich etwas getan werden soll. Sie müssen jedoch sicherstellen, dass die Synchronisierung im Umfang dieser Funktion liegt.

3
hvgotcodes

Ich bin heute auf dieses Problem gestoßen, und dies war mein naiver Versuch, bevor ich die akzeptierte Antwort sah.

<script>
    function main() {
        var a, b, c
        var one = function() {
            if ( a != undefined  && b != undefined && c != undefined ) {
                alert("Ok")
            } else {
                alert( "¬¬ ")
            }
        }

        fakeAjaxCall( function() {
            a = "two"
            one()
        } )
        fakeAjaxCall( function() {
            b = "three"
            one()
        } )
        fakeAjaxCall( function() {
            c = "four"
            one()
        } )
    }
    function fakeAjaxCall( a ) {
        a()
    }
    main()
</script>
0
OscarRyz

Ich fand einen einfacheren Weg, dies zu tun, ohne dass zusätzliche Methoden erforderlich sind, die eine Warteschlange anordnen.

JS

$.ajax({
  type: 'POST',
  url: 'ajax1.php',
  data:{
    id: 1,
    cb:'method1'//declaration of callback method of ajax1.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method1'
  eval(cb+"(JSON.stringify(data));");//here we calling method1 and pass all data
  }
});


$.ajax({
  type: 'POST',
  url: 'ajax2.php',
  data:{
    id: 2,
    cb:'method2'//declaration of callback method of ajax2.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method2'
  eval(cb+"(JSON.stringify(data));");//here we calling method2 and pass all data
  }
});


//the callback methods
function method1(data){
//here we have our data from ajax1.php
alert("method1 called with data="+data);
//doing stuff we would only do in method1
//..
}

function method2(data){
//here we have our data from ajax2.php
alert("method2 called with data="+data);
//doing stuff we would only do in method2
//..
}

PHP (ajax1.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method1'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax1"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

PHP (ajax2.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method2'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax2"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>
0
medicalbird

Ok, das ist alt, aber bitte lass mich meine Lösung einbringen :)

function sync( callback ){
    syncCount--;
    if ( syncCount < 1 ) callback();
}
function allFinished(){ .............. }

window.syncCount = 2;

$.ajax({
    url: 'url',
    success: function(data) {
        sync( allFinished );
    }
});
someFunctionWithCallback( function(){ sync( allFinished ); } )

Es funktioniert auch mit Funktionen, die einen Rückruf haben. Sie setzen den SyncCount und rufen die Funktion Sync (...) im Callback jeder Aktion auf.

0
sic

Ich habe vor einer Weile die gleiche Frage gestellt und hier ein paar gute Antworten erhalten: Beste Möglichkeit, einen 'Rückruf' nach einer Reihe asynchroner XHR-Aufrufe hinzuzufügen

0
SBUJOLD

Ich habe einige gute Tipps von den Antworten auf dieser Seite bekommen. Ich habe es ein bisschen für meinen Gebrauch angepasst und dachte, ich könnte es teilen.

// lets say we have 2 ajax functions that needs to be "synchronized". 
// In other words, we want to know when both are completed.
function foo1(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo1');
            callback();               
        }
    });
}

function foo2(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo2');
            callback();
        }
    });
}

// here is my simplified solution
ajaxSynchronizer = function() {
    var funcs = [];
    var funcsCompleted = 0;
    var callback;

    this.add = function(f) {
        funcs.Push(f);
    }

    this.synchronizer = function() {
        funcsCompleted++;
        if (funcsCompleted == funcs.length) {
            callback.call(this);
        }
    }

    this.callWhenFinished = function(cb) {
        callback = cb;
        for (var i = 0; i < funcs.length; i++) {
            funcs[i].call(this, this.synchronizer);
        }
    }
}

// this is the function that is called when both ajax calls are completed.
afterFunction = function() {
    alert('All done!');
}

// this is how you set it up
var synchronizer = new ajaxSynchronizer();
synchronizer.add(foo1);
synchronizer.add(foo2);
synchronizer.callWhenFinished(afterFunction);

Hier gibt es einige Einschränkungen, aber für meinen Fall war es in Ordnung. Ich fand auch, dass es für Fortgeschrittene ein AOP-Plugin (für jQuery) gibt, das nützlich sein könnte: http://code.google .com/p/jquery-aop/

0
PålOliver

Es ist nicht Jquery (und es scheint, dass Jquery eine praktikable Lösung hat), sondern nur als eine andere Option ....

Ich hatte ähnliche Probleme beim Arbeiten mit SharePoint-Webdiensten. Sie müssen häufig Daten aus mehreren Quellen abrufen, um Eingaben für einen einzelnen Prozess zu generieren.

Zur Lösung dieses Problems habe ich diese Funktionalität in meine Abstraktionsbibliothek AJAX eingebettet. Sie können auf einfache Weise eine Anforderung definieren, die nach Abschluss eine Reihe von Handlern auslöst. Jede Anforderung kann jedoch mit mehreren http-Aufrufen definiert werden. Hier ist die Komponente (und detaillierte Dokumentation):

DPAJAX bei DepressedPress.com

Dieses einfache Beispiel erstellt eine Anforderung mit drei Aufrufen und leitet diese Informationen dann in der Anrufreihenfolge an einen einzelnen Handler weiter:

    // The handler function 
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) }; 

    // Create the pool 
myPool = DP_AJAX.createPool(); 

    // Create the request 
myRequest = DP_AJAX.createRequest(AddUp); 

    // Add the calls to the request 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]); 

    // Add the request to the pool 
myPool.addRequest(myRequest); 

Beachten Sie, dass im Gegensatz zu vielen anderen Lösungen (einschließlich, glaube ich, die "when" -Lösung in jquery) vorausgesetzt, dass diese Methode kein einzelnes Threading der durchgeführten Aufrufe erzwingt - jede wird dennoch so schnell (oder so langsam) ausgeführt, wie es die Umgebung zulässt Der einzelne Handler wird jedoch nur aufgerufen, wenn alle abgeschlossen sind. Es unterstützt auch die Einstellung von Zeitüberschreitungswerten und Wiederholungsversuchen, wenn Ihr Dienst ein wenig unangenehm ist.

Ich fand es wahnsinnig nützlich (und aus der Perspektive des Codes unglaublich einfach zu verstehen). Keine Verkettung mehr, keine Anrufe mehr zählen und Ausgaben sparen. Einfach "einstellen und vergessen".

0
Jim Davis