jQuery passa mais parâmetros para retorno de chamada

255

Existe uma maneira de passar mais dados para uma função de retorno de chamada no jQuery?

Eu tenho duas funções e quero que o retorno de chamada para $.post, por exemplo, passe os dados resultantes da chamada AJAX, bem como alguns argumentos personalizados

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

}

Quero poder passar meus próprios parâmetros para um retorno de chamada, bem como o resultado retornado da chamada AJAX.

atp
fonte
14
Vale ressaltar que jQuery.ajax () possui uma configuração de contexto desde a versão 1.4 ( jquery14.com/day-01/jquery-14 ), veja um exemplo de uso aqui: stackoverflow.com/questions/5097191/ajax-context- opção /…
russau 05/10
Resolvi meu problema ao retornar os dados após a conclusão do AJAX e, então, faço alguma coisa.
Onaiggac

Respostas:

340

A solução é a ligação de variáveis ​​através do fechamento.


Como um exemplo mais básico, aqui está um exemplo de função que recebe e chama uma função de retorno de chamada, bem como uma função de retorno de chamada de exemplo:

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

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

Isso chama o retorno de chamada e fornece um único argumento. Agora você deseja fornecer um argumento adicional, para encerrar o retorno de chamada.

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

Ou, mais simplesmente, usando as funções de seta do ES6 :

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

Quanto ao seu exemplo específico, eu não usei a .postfunção no jQuery, mas uma rápida verificação da documentação sugere que a chamada de retorno deve ser um ponteiro de função com a seguinte assinatura:

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

Portanto, acho que a solução é a seguinte:

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

O que está acontecendo?

Na última linha, doSomething(extraStuff)é invocado e o resultado dessa invocação é um ponteiro de função .

Porque extraStuffé passado como argumento para doSomethingele, está dentro do escopo da doSomethingfunção.

Quando extraStuffé referenciado na função interna anônima retornada doSomething, é vinculado pelo fechamento do extraStuffargumento da função externa . Isso é verdade mesmo após o doSomethingretorno.

Não testei o acima, mas escrevi um código muito semelhante nas últimas 24 horas e funciona como descrevi.

É claro que você pode passar várias variáveis ​​em vez de um único objeto 'extraStuff', dependendo de suas preferências pessoais / padrões de codificação.

Bradhouse
fonte
Qual é o objetivo de 'var doSomething ='? Como isso é diferente de apenas declarar doSomething como uma função (ou seja, função doSomething (...) {})
Dave Neeley
7
Isso não é verdade. Regras de escopo diferentes se aplicam a essas duas declarações de função. O comportamento difere muito, dependendo do tempo de execução do JS (navegador de leitura). Tente também comparar o nome da função mostrado no stacktrace / breakpoint no Firebug.
Ihor Kaharlichenko
1
Ihor: Você está absolutamente correto sobre a diferença entre os dois estilos de declaração. Uma boa explicação está aqui: stackoverflow.com/questions/336859/…
bradhouse 4/11
2
Isso pode ser um pouco difícil de seguir para os usuários que tentam aplicar isso a problemas mais gerais devido à especificidade do exemplo. Adicionei um exemplo simplificado para facilitar um pouco o acompanhamento, bem como um exemplo de função de seta para impedir que essa pergunta seja fechada como uma duplicata da duplicata proposta recém-publicada. Se você sentir que minhas edições se afastaram muito da intenção original de sua resposta ou for de alguma outra forma inadequada, sinta-se à vontade para revertê-la.
4
revisão 4 desta resposta está sendo discutido na meta
bjb568
82

Ao usar doSomething(data, myDiv), você realmente chama a função e não faz referência a ela.

Você pode passar a doStomethingfunção diretamente, mas deve garantir que ela tenha a assinatura correta.

Se você deseja continuar fazendo algo do jeito que está, pode encerrar sua chamada em uma função anônima.

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

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

Dentro do código de função anônimo, você pode usar as variáveis ​​definidas no escopo em anexo. É assim que o escopo do Javascript funciona.

Vincent Robert
fonte
9
Esta é a melhor solução da IMO, por uma questão de clareza. Nenhuma return function(...){...}função extra dentro da doSomethingfunção. Encontrei outro exemplo claro do mesmo conceito aqui: theelitist.net/…
William Denniss
4
Eu acho que há um problema com este. Se seus dados extras (myDiv neste caso) forem alterados antes do retorno da chamada, você obterá o novo valor e não o antigo! No meu caso, eu estava disparando chamadas ajax para itens em uma matriz e, quando a primeira chamada de sucesso () foi executada, ela pensou que havia conseguido o último item quando foi o primeiro! a resposta de Bradhouse funcionou para mim.
Chris
@ Chris Sim, as variáveis ​​capturadas podem ser alteradas em Javascript. Se você não quiser esse comportamento, precisará criar uma função explícita para capturar o valor. O idioma mais comum é uma função auto-executável (function(myDiv){ ... })(myDiv)para capturar a variável myDiv. Isso é feito implicitamente na resposta @bradhouse.
Vincent Robert
Se você chamar esse ajax n vezes, esse método criará n funções diferentes e as passará para o retorno de chamada bem-sucedido. Isso é ruim em termos de desempenho. Abster-se desta resposta de @zeroasterisk é bom.
yılmaz 14/01
Concordo com @WilliamDenniss, IMHO este é o melhor, solução simples e clara
sebasira
52

Na verdade, é mais fácil do que todo mundo está fazendo parecer ... especialmente se você usar a $.ajax({})sintaxe básica versus uma das funções auxiliares.

Basta passar o key: valuepar como faria em qualquer objeto, ao configurar sua solicitação ajax ... (porque $(this)ainda não mudou o contexto, ainda é o gatilho para a ligação acima)

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

Uma das razões pelas quais isso é melhor do que definir o var, é que o var é global e, como tal, substituível ... se você tem duas coisas que podem acionar chamadas ajax, em teoria, você pode acioná-las mais rapidamente do que as chamadas ajax respondem e você teria o valor da segunda chamada passado para a primeira. Usar esse método acima não aconteceria (e é muito simples de usar também).

zeroasterisk
fonte
3
isto é exatamente o que eu estava procurando. Eu não sabia quando você especificou o dataType como json, ele analisou automaticamente a resposta para você. Que bom (:
Rooster
1
Parece a melhor maneira de passar parâmetros extras para ligar de volta para mim. Eu estaria interessado se alguém vê uma desvantagem com esta abordagem? Mais fácil e elegante. Obrigado!
Jan Zyka
" o var é global e, como tal, substituível ". Não é se você a declarar em uma função, que também é muito mais legível que a solução acima. Usando fechamento é uma abordagem mais limpo - veja minha resposta aqui: stackoverflow.com/a/18570951/885464
Lorenzo Polidori
5
@LorenzoPolidori Não concordo, acho a abordagem de parâmetros adicionais muito mais legível.
Andrew Plummer
2
Isso é absolutamente brilhante. Como é que eu nunca vi isso antes. Eu não preciso closure, apenas preciso que funcione e isso é absolutamente aceitável, limpo, simples e não global.
Piotr Kula
21

No mundo de hoje, há uma outra resposta mais limpa e extraída de outra resposta do 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.
    ...
}

Aqui está a pergunta e resposta originais: jQuery HOW TO ?? passar parâmetros adicionais ao retorno de chamada bem-sucedido para a chamada $ .ajax?

b01
fonte
8

Você também pode tentar algo como o seguinte:

function clicked() {

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

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

function doSomething(curData, curDiv) {

}
Rohan Almeida
fonte
5

Você pode usar um fechamento do JavaScript:

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

e quando você pode fazer:

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

Cometi um erro no último post. Este é um exemplo prático de como passar argumentos adicionais na função de retorno de chamada:

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
fonte
1
Você deve editar sua resposta original ou excluí-la antes de adicionar uma nova.
Blazemonger
3

Vamos simples! :)

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

Uma solução mais geral para enviar solicitações assíncronas usando a .ajax()API jQuery e fechamentos para passar parâmetros adicionais para a função de retorno de chamada:

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

Para mim e para outros iniciantes que acabaram de entrar em contato com Javascript,
acho que isso Closeure Solutioné meio confuso demais.

Enquanto eu descobri isso, você pode facilmente passar quantos parâmetros quiser para cada retorno de chamada ajax usando jquery.

Aqui estão duas soluções mais fáceis .

Primeiro, que o @zeroasterisk mencionou acima, exemplo:

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

Segundo, passe dados auto-definidos adicionando-os ao XHR object que existem em toda a vida útil do ajax-request-response.

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

Como você pode ver, essas duas soluções têm uma desvantagem: você precisa escrever um pouco mais de código a cada vez do que apenas escrever:

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

Leia mais sobre $ .ajax: http://api.jquery.com/jquery.ajax/

lyfing
fonte
1

Se alguém ainda vier aqui, esta é minha opinião:

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

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

O que acontece aqui, após a ligação à função de retorno de chamada, ela estará disponível na função como this .

Essa ideia vem de como o React usa a bindfuncionalidade.

Kőhalmy Zoltán
fonte
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);
}

Maneira secundária:

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

na verdade, seu código não está funcionando porque quando você escreve:

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

você coloca uma chamada de função como o terceiro parâmetro em vez de uma referência de função .

jrharshath
fonte
0

Como um adendo à resposta de b01 , o segundo argumento de $.proxyé frequentemente usado para preservar a thisreferência. Os argumentos adicionais passados ​​para $.proxysão aplicados parcialmente à função, preenchendo-a previamente com dados. Observe que qualquer argumento $.posttransmitido para o retorno de chamada será aplicado no final, portanto, doSomethingdeve haver aqueles no final de sua lista de argumentos:

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

Essa abordagem também permite que vários argumentos sejam vinculados ao retorno de chamada:

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
fonte