jQuery übergibt weitere Parameter an den Rückruf

255

Gibt es eine Möglichkeit, mehr Daten in jQuery an eine Rückruffunktion zu übergeben?

Ich habe zwei Funktionen und möchte, dass der Rückruf an die $.postbeispielsweise sowohl die resultierenden Daten des AJAX-Aufrufs als auch einige benutzerdefinierte Argumente übergibt

function clicked() {
    var myDiv = $("#my-div");
    // ERROR: Says data not defined
    $.post("someurl.php",someData,doSomething(data, myDiv),"json"); 
    // ERROR: Would pass in myDiv as curData (wrong)
    $.post("someurl.php",someData,doSomething(data, myDiv),"json"); 
}

function doSomething(curData, curDiv) {

}

Ich möchte in der Lage sein, meine eigenen Parameter an einen Rückruf sowie das vom AJAX-Aufruf zurückgegebene Ergebnis zu übergeben.

atp
quelle
14
Es ist erwähnenswert, dass jQuery.ajax () seit Version 1.4 ( jquery14.com/day-01/jquery-14 ) eine Kontexteinstellung hat. Ein Beispiel für seine Verwendung finden Sie hier: stackoverflow.com/questions/5097191/ajax-context- Option /…
Russau
Ich habe mein Problem behoben, die Daten zurückzugeben, nachdem AJAX abgeschlossen ist, und dann habe ich etwas getan.
Onaiggac

Antworten:

340

Die Lösung ist die Bindung von Variablen durch Schließen.


Als grundlegenderes Beispiel finden Sie hier eine Beispielfunktion, die eine Rückruffunktion empfängt und aufruft, sowie eine beispielhafte Rückruffunktion:

function callbackReceiver(callback) {
    callback("Hello World");
}

function callback(value1, value2) {
    console.log(value1, value2);
}

Dies ruft den Rückruf auf und liefert ein einzelnes Argument. Jetzt möchten Sie ein zusätzliches Argument angeben, sodass Sie den Rückruf zum Abschluss bringen.

callbackReceiver(callback);     // "Hello World", undefined
callbackReceiver(function(value) {
    callback(value, "Foo Bar"); // "Hello World", "Foo Bar"
});

Oder einfacher mit ES6-Pfeilfunktionen :

callbackReceiver(value => callback(value, "Foo Bar")); // "Hello World", "Foo Bar"

In Ihrem speziellen Beispiel habe ich die .postFunktion in jQuery nicht verwendet , aber ein kurzer Scan der Dokumentation legt nahe, dass der Rückruf ein Funktionszeiger mit der folgenden Signatur sein sollte:

function callBack(data, textStatus, jqXHR) {};

Daher denke ich, dass die Lösung wie folgt lautet:

var doSomething = function(extraStuff) {
    return function(data, textStatus, jqXHR) {
        // do something with extraStuff
    };
};

var clicked = function() {
    var extraStuff = {
        myParam1: 'foo',
        myParam2: 'bar'
    }; // an object / whatever extra params you wish to pass.

    $.post("someurl.php", someData, doSomething(extraStuff), "json");
};

Was ist los?

In der letzten Zeile doSomething(extraStuff)wird aufgerufen und das Ergebnis dieses Aufrufs ist ein Funktionszeiger .

Weil extraStuffals Argument übergeben wird, doSomethingliegt im Rahmen der doSomethingFunktion.

Wenn extraStuffin der zurückgegebenen anonymen inneren Funktion darauf verwiesen doSomethingwird, ist dies durch Schließen an das extraStuffArgument der äußeren Funktion gebunden . Dies gilt auch nach doSomethingder Rückkehr.

Ich habe das oben genannte nicht getestet, aber ich habe in den letzten 24 Stunden sehr ähnlichen Code geschrieben und er funktioniert wie beschrieben.

Sie können natürlich mehrere Variablen anstelle eines einzelnen 'extraStuff'-Objekts übergeben, abhängig von Ihren persönlichen Vorlieben / Codierungsstandards.

Bradhouse
quelle
Was ist der Zweck von 'var doSomething ='? Wie unterscheidet sich das von der bloßen Deklaration von doSomething als Funktion (dh Funktion doSomething (...) {})
Dave Neeley
7
Das ist nicht wahr. Für diese beiden Funktionsdeklarationen gelten unterschiedliche Bereichsregeln. Das Verhalten ist je nach JS-Laufzeit (Browser lesen) sehr unterschiedlich. Versuchen Sie auch, den in Stacktrace / Breakpoint in Firebug angezeigten Funktionsnamen zu vergleichen.
Ihor Kaharlichenko
1
Ihor: Sie haben absolut Recht mit dem Unterschied zwischen den beiden Deklarationsstilen. Eine gute Erklärung ist hier: stackoverflow.com/questions/336859/…
Bradhouse
2
Dies kann für Benutzer, die versuchen, dies auf allgemeinere Probleme anzuwenden, aufgrund der Spezifität des Beispiels etwas schwierig zu befolgen sein. Ich habe ein vereinfachtes Beispiel hinzugefügt, um das Verfolgen zu vereinfachen, sowie ein Pfeilfunktionsbeispiel, um zu verhindern, dass diese Frage als Duplikat des neu veröffentlichten vorgeschlagenen Duplikats geschlossen wird. Wenn Sie der Meinung sind, dass meine Änderungen zu weit von der ursprünglichen Absicht Ihrer Antwort abweichen oder auf andere Weise unangemessen sind, können Sie sie jederzeit zurücksetzen.
4
Revision 4 dieser Antwort wird auf meta
bjb568
82

Bei der Verwendung doSomething(data, myDiv)rufen Sie die Funktion tatsächlich auf und verweisen nicht darauf.

Sie können die doStomethingFunktion entweder direkt übergeben, aber Sie müssen sicherstellen, dass sie die richtige Signatur hat.

Wenn Sie doSomething so lassen möchten, wie es ist, können Sie den Aufruf in eine anonyme Funktion einschließen.

function clicked() {
    var myDiv = $("#my-div");
    $.post("someurl.php",someData, function(data){ 
      doSomething(data, myDiv)
    },"json"); 
}

function doSomething(curData, curDiv) {
    ...
}

Innerhalb des anonymen Funktionscodes können Sie die im umschließenden Bereich definierten Variablen verwenden. So funktioniert das Javascript-Scoping.

Vincent Robert
quelle
9
Dies ist aus Gründen der Klarheit die beste Lösung IMO. Keine zusätzliche return function(...){...}Funktion innerhalb der doSomethingFunktion. Ich habe hier ein weiteres klares Beispiel für dasselbe Konzept gefunden: theelitist.net/…
William Denniss
4
Ich denke, es gibt ein Problem mit diesem. Wenn sich Ihre zusätzlichen Daten (in diesem Fall myDiv) ändern, bevor der Rückruf ausgelöst wird, erhalten Sie den neuen Wert und nicht den alten! In meinem Fall habe ich Ajax-Aufrufe für Elemente in einem Array ausgelöst, und als der erste Aufruf von success () ausgeführt wurde, glaubte er, dass das letzte Element erfolgreich war, als es das erste war! Bradhouse Antwort hat für mich funktioniert.
Chris
@Chris Ja, erfasste Variablen können in Javascript geändert werden. Wenn Sie dieses Verhalten nicht möchten, müssen Sie eine explizite Funktion erstellen, um den Wert zu erfassen. Die üblichste Redewendung ist eine selbstausführende Funktion (function(myDiv){ ... })(myDiv)zum Erfassen der Variablen myDiv. Dies geschieht implizit in der Antwort von @bradhouse.
Vincent Robert
Wenn Sie diesen Ajax n-mal aufrufen, erstellt diese Methode n verschiedene Funktionen und übergibt sie an den erfolgreichen Rückruf. Das ist schlecht in Bezug auf die Leistung. Die Antwort von @zeroasterisk zu unterlassen ist gut.
Yılmaz
Ich stimme @WilliamDenniss zu, IMHO ist dies die beste, einfachste und
klarste
52

Es ist tatsächlich einfacher, als jeder es klingen lässt ... besonders wenn Sie die Basissyntax im Vergleich zu $.ajax({})einer der Hilfsfunktionen verwenden.

Übergeben Sie das key: valuePaar einfach wie bei jedem Objekt, wenn Sie Ihre Ajax-Anfrage einrichten ... (da $(this)der Kontext noch nicht geändert wurde, ist es immer noch der Auslöser für den obigen Bindungsaufruf.)

<script type="text/javascript">
$(".qty input").bind("keypress change", function() {
    $.ajax({
        url: "/order_items/change/"+$(this).attr("data-order-item-id")+"/qty:"+$(this).val()+"/returnas.json",
        type: "POST",
        dataType: "json",
        qty_input: $(this),
        anything_else_i_want_to_pass_in: "foo",
        success: function(json_data, textStatus, jqXHR) {
            /* here is the input, which triggered this AJAX request */
            console.log(this.qty_input);
            /* here is any other parameter you set when initializing the ajax method */
            console.log(this.anything_else_i_want_to_pass_in);
        }
    });
});
</script>

Einer der Gründe, warum dies besser ist als das Festlegen der Variable, ist, dass die Variable global und als solche überschreibbar ist. Wenn Sie zwei Dinge haben, die Ajax-Aufrufe auslösen können, können Sie sie theoretisch schneller auslösen, als der Ajax-Anruf antwortet Sie hätten den Wert für den zweiten Anruf an den ersten übergeben. Mit dieser Methode oben würde das nicht passieren (und es ist auch ziemlich einfach zu verwenden).

Zeroasterisk
quelle
3
Genau das habe ich gesucht. Ich wusste nicht, wann Sie den Datentyp als json angegeben haben. Er hat die Antwort automatisch für Sie analysiert. Das schöne (:
Hahn
1
Klingt nach der besten Möglichkeit, zusätzliche Parameter an mich zurückzurufen. Es würde mich interessieren, ob jemand einen Nachteil bei diesem Ansatz sieht? Am einfachsten und elegantesten. Vielen Dank!
Jan Zyka
" Die Variable ist global und als solche überschreibbar ". Es ist nicht so, dass Sie es innerhalb einer Funktion deklarieren, die auch viel besser lesbar ist als die obige Lösung. Die Verwendung von Verschlüssen ist ein sauberer Ansatz - siehe meine Antwort hier: stackoverflow.com/a/18570951/885464
Lorenzo Polidori
5
@ LorenzoPolidori Ich bin anderer Meinung, ich finde den zusätzlichen Parameteransatz weitaus lesbarer.
Andrew Plummer
2
Das ist absolut genial. Wieso habe ich das noch nie gesehen? Ich brauche es nicht closureIch brauche es nur um zu arbeiten und das ist absolut ace, sauber, einfach und nicht global.
Piotr Kula
21

In der heutigen Welt gibt es eine andere Antwort, die sauberer ist und einer anderen Antwort auf den Stapelüberlauf entnommen ist:

function clicked()
{
    var myDiv = $( "#my-div" );

    $.post( "someurl.php", {"someData": someData}, $.proxy(doSomething, myDiv), "json" );
}

function doSomething( data )
{
    // this will be equal to myDiv now. Thanks to jQuery.proxy().
    var $myDiv = this;

    // doing stuff.
    ...
}

Hier ist die ursprüngliche Frage und Antwort: jQuery WIE GEHT? zusätzliche Parameter an den erfolgreichen Rückruf für einen $ .ajax-Aufruf übergeben?

b01
quelle
8

Sie können auch Folgendes ausprobieren:

function clicked() {

    var myDiv = $("#my-div");

    $.post("someurl.php",someData,function(data){
        doSomething(data, myDiv);
    },"json"); 
}

function doSomething(curData, curDiv) {

}
Rohan Almeida
quelle
5

Sie können einen Abschluss von JavaScript verwenden:

function wrapper( var1, var2,....) // put here your variables
{
  return function( data, status)
  {
     //Handle here results of call
  }
};

und wann du kannst:

$.post("someurl.php",data,wrapper(var1, var2, etc...),"html");
Artem Barger
quelle
3

Ich habe im letzten Beitrag einen Fehler gemacht. Dies ist ein funktionierendes Beispiel für die Übergabe eines zusätzlichen Arguments in der Rückruffunktion:

function custom_func(p1,p2) {
    $.post(AJAX_FILE_PATH,{op:'dosomething',p1:p1},
        function(data){
            return function(){
                alert(data);
                alert(p2);
            }(data,p2)
        }
    );
    return false;
}
Igor
quelle
1
Sie sollten entweder Ihre ursprüngliche Antwort bearbeiten oder sie löschen, bevor Sie eine neue hinzufügen.
Blazemonger
3

Lass uns einfach gehen! :) :)

$.ajax({
    url: myUrl,
    context: $this, // $this == Current $element
    success: function(data) {
        $.proxy(publicMethods.update, this)(data); // this == Current $element
    }
});
Molokoloco
quelle
2

Eine allgemeinere Lösung zum Senden asynchroner Anforderungen mithilfe der .ajax()jQuery-API und von Schließungen, um zusätzliche Parameter an die Rückruffunktion zu übergeben:

function sendRequest(method, url, content, callback) {
    // additional data for the callback
    var request = {
        method: method,
        url: url
    };

    $.ajax({
        type: method,
        url: url,
        data: content
     }).done(function(data, status, xhr) {
        if (callback) callback(xhr.status, data, request);
     }).fail(function(xhr, status) {
        if (callback) callback(xhr.status, xhr.response, request);
     });
};
Lorenzo Polidori
quelle
1

Für mich und andere Neulinge, die sich gerade mit Javascript in Verbindung gesetzt haben,
finde ich das Closeure Solutionetwas zu verwirrend.

Während ich das gefunden habe, können Sie mit jquery problemlos so viele Parameter wie Sie möchten an jeden Ajax-Rückruf übergeben.

Hier sind zwei einfachere Lösungen .

Erste, die @zeroasterisk oben erwähnt hat, Beispiel:

var $items = $('.some_class');
$.each($items, function(key, item){
    var url = 'http://request_with_params' + $(item).html();
    $.ajax({
        selfDom     : $(item),
        selfData    : 'here is my self defined data',

        url         : url,
        dataType    : 'json',
        success     : function(data, code, jqXHR){
            // in $.ajax callbacks, 
            // [this] keyword references to the options you gived to $.ajax
            // if you had not specified the context of $.ajax callbacks.
            // see http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings context
            var $item = this.selfDom;
            var selfdata = this.selfData;
            $item.html( selfdata );
            ...
        } 
    });
});

Zweitens: Übergeben Sie selbst definierte Daten, indem Sie sie zu den Daten hinzufügen, XHR object die in der gesamten Lebensdauer von Ajax-Anfrage-Antwort vorhanden sind.

var $items = $('.some_class');
$.each($items, function(key, item){
    var url = 'http://request_with_params' + $(item).html();
    $.ajax({
        url         : url,
        dataType    : 'json',
        beforeSend  : function(XHR) {
            // 为了便于回调,把当前的 jquery对象集存入本次 XHR
            XHR.selfDom = $(item);
            XHR.selfData = 'here is my self defined data';
        },
        success     : function(data, code, jqXHR){
            // jqXHR is a superset of the browser's native XHR object
            var $item = jqXHR.selfDom;
            var selfdata = jqXHR.selfData;
            $item.html( selfdata );
            ...
        } 
    });
});

Wie Sie sehen können, haben diese beiden Lösungen den Nachteil, dass Sie jedes Mal etwas mehr Code schreiben müssen als nur schreiben:

$.get/post (url, data, successHandler);

Lesen Sie mehr über $ .ajax: http://api.jquery.com/jquery.ajax/

lyfing
quelle
1

Wenn noch jemand hierher kommt, ist dies meine Meinung:

$('.selector').click(myCallbackFunction.bind({var1: 'hello', var2: 'world'}));

function myCallbackFunction(event) {
    var passedArg1 = this.var1,
        passedArg2 = this.var2
}

Was hier passiert, ist nach dem Binden an die Rückruffunktion innerhalb der Funktion als verfügbar this.

Diese Idee stammt davon, wie React die bindFunktionalität nutzt .

Kőhalmy Zoltán
quelle
1
$(document).on('click','[action=register]',function(){
    registerSocket(registerJSON(),registerDone,second($(this)));
});

function registerSocket(dataFn,doneFn,second){
                 $.ajax({
                       type:'POST',
                       url: "http://localhost:8080/store/public/register",
                       contentType: "application/json; charset=utf-8",
                       dataType: "json",
                       data:dataFn
                 }).done ([doneFn,second])
                   .fail(function(err){
                            console.log("AJAX failed: " + JSON.stringify(err, null, 2));
                        });
}

function registerDone(data){
    console.log(JSON.stringify(data));
}
function second(element){
    console.log(element);
}

Sekundärer Weg:

function socketWithParam(url,dataFn,doneFn,param){
  $.ajax({
    type:'POST',
    url:url,
    contentType: "application/json; charset=utf-8",
    headers: { 'Authorization': 'Bearer '+localStorage.getItem('jwt')},
    data:dataFn
    }).done(function(data){
      doneFn(data,param);
    })
    .fail(function(err,status,xhr){
    console.log("AJAX failed: " + JSON.stringify(err, null, 2));
    });
}

$(document).on('click','[order-btn]',function(){
  socketWithParam(url,fakeDataFn(),orderDetailDone,secondParam);
});

function orderDetailDone(data,param){
  -- to do something --
}
Musa
quelle
0

Tatsächlich funktioniert Ihr Code nicht, weil beim Schreiben:

$.post("someurl.php",someData,doSomething(data, myDiv),"json"); 

Sie platzieren einen Funktionsaufruf als dritten Parameter und nicht als Funktionsreferenz .

jrharshath
quelle
0

Als Ergänzung zur Antwort von b01$.proxy wird häufig das zweite Argument von verwendet, um die thisReferenz zu erhalten . Zusätzliche Argumente, die an übergeben $.proxywerden , werden teilweise auf die Funktion angewendet und füllen sie vorab mit Daten. Beachten Sie, dass alle Argumente, $.postdie an den Rückruf übergeben werden, am Ende angewendet werden. Daher doSomethingsollten diese am Ende der Argumentliste stehen:

function clicked() {
    var myDiv = $("#my-div");
    var callback = $.proxy(doSomething, this, myDiv);
    $.post("someurl.php",someData,callback,"json"); 
}

function doSomething(curDiv, curData) {
    //"this" still refers to the same "this" as clicked()
    var serverResponse = curData;
}

Mit diesem Ansatz können auch mehrere Argumente an den Rückruf gebunden werden:

function clicked() {
    var myDiv = $("#my-div");
    var mySpan = $("#my-span");
    var isActive = true;
    var callback = $.proxy(doSomething, this, myDiv, mySpan, isActive);
    $.post("someurl.php",someData,callback,"json"); 
}

function doSomething(curDiv, curSpan, curIsActive, curData) {
    //"this" still refers to the same "this" as clicked()
    var serverResponse = curData;
}
Hyperslug
quelle