jQuery passa più parametri al callback


256

C'è un modo per passare più dati in una funzione di callback in jQuery?

Ho due funzioni e voglio che il callback $.post, ad esempio, passi sia i dati risultanti della chiamata AJAX, sia alcuni argomenti personalizzati

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) {

}

Voglio essere in grado di passare i miei parametri a una richiamata, così come il risultato restituito dalla chiamata AJAX.


14
Vale la pena ricordare che jQuery.ajax () ha un'impostazione di contesto dalla versione 1.4 ( jquery14.com/day-01/jquery-14 ) vedi un esempio del suo utilizzo qui: stackoverflow.com/questions/5097191/ajax-context- opzione /…
russau

Ho risolto il mio problema restituendo i dati dopo che AJAX è stato completato e poi ho fatto qualcosa.
Onaiggac

Risposte:


341

La soluzione è il legame delle variabili attraverso la chiusura.


Come esempio più semplice, ecco una funzione di esempio che riceve e chiama una funzione di callback, così come una funzione di callback di esempio:

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

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

Ciò chiama la richiamata e fornisce un singolo argomento. Ora vuoi fornire un argomento aggiuntivo, quindi concludi il callback.

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

O, più semplicemente usando le funzioni freccia ES6 :

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

Per quanto riguarda il tuo esempio specifico, non ho usato la .postfunzione in jQuery, ma una rapida scansione della documentazione suggerisce che la richiamata dovrebbe essere un puntatore a funzione con la seguente firma:

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

Pertanto penso che la soluzione sia la seguente:

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

Che cosa sta succedendo?

Nell'ultima riga, doSomething(extraStuff)viene richiamato e il risultato di tale invocazione è un puntatore a funzione .

Perché extraStuffviene passato come argomento ad doSomethingesso rientra nell'ambito della doSomethingfunzione.

Quando extraStuffviene fatto riferimento nella funzione interna anonima restituita doSomething, è vincolato dalla chiusura extraStuffall'argomento della funzione esterna . Questo è vero anche dopo che doSomethingè tornato.

Non ho testato quanto sopra, ma ho scritto un codice molto simile nelle ultime 24 ore e funziona come ho descritto.

Ovviamente puoi passare più variabili invece di un singolo oggetto 'extraStuff' a seconda delle tue preferenze personali / standard di codifica.


Qual è lo scopo di "var doSomething ="? In che modo è diverso dal dichiarare semplicemente doSomething come una funzione (cioè funzione doSomething (...) {})
Dave Neeley,

7
Non è vero. Regole di ambito diverse si applicano a queste due dichiarazioni di funzione. Il comportamento è molto diverso a seconda del runtime JS (browser di lettura). Prova anche a confrontare il nome della funzione mostrato in stacktrace / breakpoint in Firebug.
Ihor Kaharlichenko

1
Ihor: Hai assolutamente ragione riguardo alla differenza tra i due stili di dichiarazione. Una spiegazione buona è qui: stackoverflow.com/questions/336859/...
bradhouse

2
Questo può essere un po 'difficile da seguire per gli utenti che cercano di applicarlo a problemi più generali a causa della specificità dell'esempio. Ho aggiunto un esempio semplificato per renderlo un po 'più facile da seguire, nonché un esempio di funzione freccia per evitare che questa domanda venga chiusa come duplicato del duplicato proposto di recente. Se ritieni che le mie modifiche si siano discostate troppo dall'intento originale della tua risposta o siano in qualsiasi altro modo inappropriate, non esitare a ripristinarle.

4
la revisione 4 di questa risposta è in discussione su meta
bjb568

82

Quando si utilizza doSomething(data, myDiv), si chiama effettivamente la funzione e non si fa riferimento ad essa.

Puoi passare la doStomethingfunzione direttamente ma devi assicurarti che abbia la firma corretta.

Se vuoi continuare a fare qualcosa così com'è, puoi racchiudere la sua chiamata in una funzione anonima.

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

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

All'interno del codice della funzione anonimo, è possibile utilizzare le variabili definite nell'ambito di inclusione. Questo è il modo in cui funziona l'ambito Javascript.


9
Questa è la migliore soluzione IMO, per motivi di chiarezza. Nessuna return function(...){...}funzione aggiuntiva all'interno della doSomethingfunzione. Ho trovato un altro chiaro esempio dello stesso concetto qui: theelitist.net/…
William Denniss

4
Penso che ci sia un problema con questo. Se i tuoi dati extra (myDiv in questo caso) cambiano prima che venga attivato il callback, ottieni il nuovo valore e non quello vecchio! Nel mio caso, stavo sparando chiamate ajax su elementi in un array, e quando è stata eseguita la prima chiamata success (), pensava di aver avuto successo sull'ultimo elemento quando era il primo! La risposta di Bradhouse ha funzionato per me.
Chris

@Chris Sì, le variabili catturate possono essere modificate in Javascript. Se non vuoi questo comportamento, devi creare una funzione esplicita per catturare il valore. L'idioma più comune è una funzione autoeseguita (function(myDiv){ ... })(myDiv)per catturare la variabile myDiv. Questo viene fatto implicitamente nella risposta @bradhouse.
Vincent Robert

Se lo chiami ajax n volte, questo metodo crea n diverse funzioni e le passa al callback di successo. Questo è negativo in termini di prestazioni. Per trattenere questo @zeroasterisk la risposta è buona.
yılmaz

Sono d'accordo con @WilliamDenniss, IMHO questa è la soluzione migliore, semplice e chiara
sebasira

52

In realtà è più facile che tutti lo facciano sembrare ... specialmente se usi la $.ajax({})sintassi di base rispetto a una delle funzioni di supporto.

Basta passare la key: valuecoppia come faresti su qualsiasi oggetto, quando imposti la tua richiesta ajax ... (perché $(this)non ha ancora cambiato contesto, è ancora il trigger per la chiamata di bind sopra)

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

Uno dei motivi per cui è meglio che impostare var, è che var è globale e come tale sovrascrivibile ... se hai 2 cose che possono attivare chiamate ajax, potresti in teoria attivarle più velocemente di quanto la chiamata ajax risponde, e faresti passare il valore per la seconda chiamata alla prima. Utilizzando questo metodo, sopra, ciò non accadrebbe (ed è anche abbastanza semplice da usare).


3
Questo e 'esattamente quello che stavo cercando. Non sapevo quando hai specificato dataType come json, ha analizzato automaticamente la risposta per te. Che carino (:
Gallo

1
Mi sembra il modo migliore per passare parametri extra alla richiamata. Sarei interessato se qualcuno vedesse un inconveniente con questo approccio? Più semplice ed elegante. Grazie!
Jan Zyka

" la var è globale e come tale sovrascrivibile ". Non lo è se lo dichiari all'interno di una funzione, che è anche molto più leggibile della soluzione sopra. L'uso delle chiusure è un approccio più pulito - vedi la mia risposta qui: stackoverflow.com/a/18570951/885464
Lorenzo Polidori

5
@LorenzoPolidori Non sono d'accordo, trovo l'approccio ai parametri aggiuntivi molto più leggibile.
Andrew Plummer

2
Questo è assolutamente geniale. Come mai non l'ho mai visto prima. Non ho bisogno, closureho solo bisogno che funzioni e questo è assolutamente eccezionale, pulito, semplice e non globale.
Piotr Kula

21

Nel mondo di oggi c'è un'altra risposta più pulita e presa da un'altra risposta di Stack Overflow:

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

Ecco la domanda e la risposta originali: jQuery HOW TO ?? passare parametri aggiuntivi al callback di successo per la chiamata $ .ajax?


8

Puoi anche provare qualcosa di simile:

function clicked() {

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

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

function doSomething(curData, curDiv) {

}

5

Puoi utilizzare una chiusura di JavaScript:

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

e quando puoi fare:

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

3

Ho fatto un errore nell'ultimo mio post. Questo è un esempio funzionante su come passare argomenti aggiuntivi nella funzione di callback:

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

1
È necessario modificare la risposta originale o eliminarla prima di aggiungerne una nuova.
Blazemonger

3

Andiamo semplice! :)

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

2

Una soluzione più generale per l'invio di richieste asincrone utilizzando l' .ajax()API jQuery e le chiusure per passare parametri aggiuntivi alla funzione di callback:

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

1

Per me e altri neofiti che hanno appena contattato Javascript,
penso che Closeure Solutionsia un po 'troppo confuso.

Anche se l'ho scoperto, puoi passare facilmente tutti i parametri che desideri a ogni callback ajax usando jquery.

Ecco due soluzioni più semplici .

Il primo, che @zeroasterisk ha menzionato sopra, esempio:

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

Secondo, passa i dati auto-definiti aggiungendoli a quelli XHR object presenti nell'intera durata di vita di ajax-richiesta-risposta.

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

Come puoi vedere queste due soluzioni hanno uno svantaggio che: devi scrivere un po 'più di codice ogni volta che scrivere semplicemente:

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

Ulteriori informazioni su $ .ajax: http://api.jquery.com/jquery.ajax/


1

Se qualcuno viene ancora qui, questa è la mia opinione:

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

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

Quello che succede qui, dopo l'associazione alla funzione di callback, sarà disponibile all'interno della funzione come this.

Questa idea deriva da come React utilizza la bindfunzionalità.


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

Modo secondario:

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

0

in realtà, il tuo codice non funziona perché quando scrivi:

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

si inserisce una chiamata di funzione come terzo parametro anziché come riferimento a una funzione .


0

In aggiunta alla risposta di b01 , il secondo argomento di $.proxyviene spesso utilizzato per preservare il thisriferimento. Gli argomenti aggiuntivi passati a $.proxyvengono parzialmente applicati alla funzione, precompilandola con i dati. Nota che tutti gli argomenti $.postpassati alla richiamata verranno applicati alla fine, quindi doSomethingdovrebbero avere quelli alla fine del suo elenco di argomenti:

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

Questo approccio consente anche di associare più argomenti al callback:

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.