jQuery meneruskan lebih banyak parameter ke dalam panggilan balik

255

Apakah ada cara untuk melewatkan lebih banyak data ke fungsi panggilan balik di jQuery?

Saya memiliki dua fungsi dan saya ingin panggilan balik ke $.post, misalnya, untuk meneruskan data yang dihasilkan dari panggilan AJAX, serta beberapa argumen khusus

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

}

Saya ingin dapat meneruskan parameter saya sendiri ke panggilan balik, serta hasilnya dikembalikan dari panggilan AJAX.

atp
sumber
14
Perlu disebutkan bahwa jQuery.ajax () telah memiliki pengaturan konteks sejak ver 1.4 ( jquery14.com/day-01/jquery-14 ) lihat contoh penggunaannya di sini: stackoverflow.com/questions/5097191/ajax-context- option / ...
russau
Saya memindahkan masalah saya mengembalikan data setelah AJAX selesai dan kemudian saya melakukan sesuatu.
Onaiggac

Jawaban:

340

Solusinya adalah pengikatan variabel melalui penutupan.


Sebagai contoh yang lebih mendasar, berikut adalah contoh fungsi yang menerima dan memanggil fungsi panggilan balik, serta contoh fungsi panggilan balik:

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

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

Ini memanggil panggilan balik dan memasok satu argumen. Sekarang Anda ingin memberikan argumen tambahan, sehingga Anda menutup callback sebagai penutup.

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

Atau, lebih sederhana menggunakan Fungsi Panah ES6 :

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

Adapun contoh spesifik Anda, saya belum menggunakan .postfungsi di jQuery, tetapi pemindaian cepat dokumentasi menunjukkan panggilan kembali harus menjadi penunjuk fungsi dengan tanda tangan berikut:

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

Karena itu saya pikir solusinya adalah sebagai berikut:

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

Apa yang terjadi?

Di baris terakhir, doSomething(extraStuff)yang dipanggil dan hasil doa yang merupakan pointer fungsi .

Karena extraStuffdilewatkan sebagai argumen untuk doSomethingitu berada dalam lingkup doSomethingfungsi.

Ketika extraStuffdireferensikan dalam fungsi batin anonim yang dikembalikan doSomethingitu terikat oleh penutupan dengan extraStuffargumen fungsi luar itu . Ini benar bahkan setelah doSomethingkembali.

Saya belum menguji di atas, tetapi saya telah menulis kode yang sangat mirip dalam 24 jam terakhir dan berfungsi seperti yang saya jelaskan.

Anda tentu saja dapat melewati beberapa variabel, bukan objek 'ekstraStuff' tunggal tergantung pada preferensi pribadi Anda / standar pengkodean.

bradhouse
sumber
Apa tujuan dari 'var doSomething ='? Apa bedanya dengan hanya mendeklarasikan doSomething sebagai fungsi (yaitu fungsi doSomething (...) {})
Dave Neeley
7
Itu tidak benar. Aturan cakupan yang berbeda berlaku untuk dua deklarasi fungsi ini. Perilaku ini sangat berbeda tergantung pada runtime JS (baca browser). Coba juga untuk membandingkan nama fungsi yang ditampilkan di stacktrace / breakpoint di Firebug.
Ihor Kaharlichenko
1
Ihor: Anda benar tentang perbedaan antara kedua gaya deklarasi. Penjelasan yang baik ada di sini: stackoverflow.com/questions/336859/…
bradhouse
2
Ini mungkin agak sulit untuk diikuti bagi pengguna yang mencoba menerapkan ini pada masalah yang lebih umum karena kekhususan contoh. Saya telah menambahkan contoh yang disederhanakan membuatnya sedikit lebih mudah untuk diikuti, serta contoh fungsi panah untuk mencegah pertanyaan ini ditutup sebagai duplikat dari duplikat yang diusulkan yang baru diposting. Jika Anda merasa suntingan saya menyimpang terlalu jauh dari maksud asli jawaban Anda, atau dengan cara lain tidak pantas, jangan ragu untuk mengembalikannya.
4
revisi 4 dari jawaban ini sedang dibahas di meta
bjb568
82

Saat menggunakan doSomething(data, myDiv), Anda benar-benar memanggil fungsi dan tidak membuat referensi untuk itu.

Anda dapat melewati doStomethingfungsi secara langsung tetapi Anda harus memastikannya memiliki tanda tangan yang benar.

Jika Anda ingin tetap melakukan sesuatu seperti itu, Anda dapat membungkus panggilannya dalam fungsi anonim.

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

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

Di dalam kode fungsi anonim, Anda dapat menggunakan variabel yang ditentukan dalam lingkup terlampir. Ini adalah cara pelingkupan Javascript bekerja.

Vincent Robert
sumber
9
Ini adalah solusi IMO terbaik, demi kejelasan. Tidak ada return function(...){...}fungsi tambahan di dalam doSomethingfungsi. Saya menemukan contoh jelas lain dari konsep yang sama di sini: theelitist.net/...
William Denniss
4
Saya pikir ada masalah dengan ini. Jika data tambahan Anda (myDiv dalam kasus ini) berubah sebelum panggilan balik diaktifkan, Anda mendapatkan nilai baru dan bukan yang lama! Dalam kasus saya, saya meluncurkan panggilan ajax pada item dalam sebuah array, dan pada saat panggilan pertama () berhasil dieksekusi, ia berpikir telah berhasil pada item terakhir ketika itu yang pertama! jawaban bradhouse bekerja untuk saya.
Chris
@ Chris Ya, variabel yang ditangkap dapat diubah dalam Javascript. Jika Anda tidak ingin perilaku ini, Anda perlu membuat fungsi eksplisit untuk menangkap nilai. Idiom yang paling umum adalah fungsi mengeksekusi diri (function(myDiv){ ... })(myDiv)untuk menangkap variabel myDiv. Ini secara implisit dilakukan dalam jawaban @bradhouse.
Vincent Robert
Jika Anda memanggil ini ajax n kali, metode ini menciptakan n fungsi yang berbeda dan meneruskannya ke panggilan balik yang berhasil. Ini buruk dalam hal kinerja. Untuk menahan jawaban @ zeroasterisk ini bagus.
yılmaz
Saya setuju dengan @ WilliamDenniss, IMHO ini adalah solusi terbaik, sederhana dan jelas
sebasira
52

Ini sebenarnya lebih mudah daripada semua orang membuatnya terdengar ... terutama jika Anda menggunakan $.ajax({})sintaks dasar vs salah satu fungsi pembantu.

Cukup berikan key: valuepasangan seperti pada objek apa pun, ketika Anda mengatur permintaan ajax Anda ... (karena $(this)belum mengubah konteks, itu masih menjadi pemicu untuk panggilan bind di atas)

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

Salah satu alasan ini lebih baik daripada mengatur var, adalah bahwa var bersifat global dan karena itu, ditimpa ... jika Anda memiliki 2 hal yang dapat memicu panggilan ajax, secara teori Anda dapat memicu mereka lebih cepat daripada merespon panggilan ajax, dan Anda akan memiliki nilai untuk panggilan kedua masuk ke yang pertama. Menggunakan metode ini, di atas, itu tidak akan terjadi (dan itu cukup mudah digunakan juga).

zeroasterisk
sumber
3
ini persis apa yang saya cari. Saya tidak tahu kapan Anda menentukan tipe data sebagai json itu secara otomatis mem-parsing respons untuk Anda. Itu bagus (:
Ayam
1
Kedengarannya seperti cara terbaik untuk lulus params ekstra untuk menelepon kembali kepada saya. Saya akan tertarik jika ada yang melihat kelemahan dengan pendekatan ini? Termudah dan paling elegan. Terima kasih!
Jan Zyka
" var bersifat global dan karenanya, dapat ditimpa ". Bukan jika Anda mendeklarasikannya dalam suatu fungsi, yang juga jauh lebih mudah dibaca daripada solusi di atas. Menggunakan penutupan adalah pendekatan yang lebih bersih - lihat jawaban saya di sini: stackoverflow.com/a/18570951/885464
Lorenzo Polidori
5
@LorenzoPolidori Saya tidak setuju, saya menemukan pendekatan parameter tambahan jauh lebih mudah dibaca.
Andrew Plummer
2
Ini sangat brilian. Kenapa saya tidak pernah melihat ini sebelumnya. Saya tidak perlu, closuresaya hanya perlu untuk bekerja dan ini benar-benar kartu as, bersih, sederhana dan non global.
Piotr Kula
21

Di dunia sekarang ini ada jawaban lain yang lebih bersih, dan diambil dari jawaban Stack Overflow lainnya:

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

Inilah pertanyaan dan jawaban asli: jQuery BAGAIMANA CARA ?? lulus parameter tambahan untuk panggilan balik sukses untuk $ .ajax panggilan?

b01
sumber
8

Anda juga dapat mencoba sesuatu seperti berikut ini:

function clicked() {

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

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

function doSomething(curData, curDiv) {

}
Rohan Almeida
sumber
5

Anda dapat menggunakan penutupan JavaScript:

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

dan kapan Anda bisa melakukannya:

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

Saya telah membuat kesalahan dalam posting terakhir saya. Ini adalah contoh yang berfungsi untuk cara menyampaikan argumen tambahan dalam fungsi panggilan balik:

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
sumber
1
Anda harus mengedit jawaban asli Anda, atau menghapusnya sebelum menambahkan yang baru.
Blazemonger
3

Ayo sederhana! :)

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

Solusi yang lebih umum untuk mengirim permintaan asinkron menggunakan .ajax()API jQuery dan penutupan untuk meneruskan parameter tambahan ke fungsi panggilan balik:

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

Bagi saya, dan pemula lainnya yang baru saja kontak dengan Javascript,
saya pikir itu Closeure Solutionagak terlalu membingungkan.

Sementara saya menemukan itu, Anda dapat dengan mudah melewati banyak parameter yang Anda inginkan untuk setiap panggilan balik ajax menggunakan jquery.

Berikut adalah dua solusi yang lebih mudah .

Pertama, yang @zeroasterisk sebutkan di atas, contoh:

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

Yang kedua, berikan data yang didefinisikan sendiri dengan menambahkannya ke data XHR object yang ada di seluruh rentang kehidupan 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 );
            ...
        } 
    });
});

Seperti yang Anda lihat, dua solusi ini memiliki kelemahan: Anda perlu menulis lebih banyak kode setiap kali daripada hanya menulis:

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

Baca lebih lanjut tentang $ .ajax: http://api.jquery.com/jquery.ajax/

lyfing
sumber
1

Jika seseorang masih datang ke sini, ini adalah pendapat saya:

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

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

Apa yang terjadi di sini, setelah mengikat ke fungsi panggilan balik, itu akan tersedia dalam fungsi sebagai this.

Ide ini berasal dari bagaimana Bereaksi menggunakan bindfungsionalitas.

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

Cara sekunder:

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

sebenarnya, kode Anda tidak berfungsi karena ketika Anda menulis:

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

Anda menempatkan panggilan fungsi sebagai parameter ketiga daripada referensi fungsi .

jrharshath
sumber
0

Sebagai tambahan untuk jawaban b01 , argumen kedua $.proxysering digunakan untuk mempertahankan thisreferensi. Argumen tambahan yang diteruskan ke $.proxysebagian diterapkan ke fungsi, pra-mengisinya dengan data. Perhatikan bahwa argumen apa pun yang $.postlolos ke panggilan balik akan diterapkan di akhir, jadi doSomethingharus ada argumen di akhir daftar argumennya:

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

Pendekatan ini juga memungkinkan beberapa argumen terikat ke 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;
}
hyperslug
sumber