jQuery ส่งพารามิเตอร์เพิ่มเติมไปที่ callback


255

มีวิธีการส่งผ่านข้อมูลมากขึ้นในฟังก์ชั่นการโทรกลับใน jQuery หรือไม่?

ฉันมีสองฟังก์ชั่นและฉันต้องการให้โทรกลับไปที่$.postตัวอย่างเช่นเพื่อส่งผ่านทั้งข้อมูลผลลัพธ์ของการโทร AJAX เช่นเดียวกับข้อโต้แย้งที่กำหนดเองบางอย่าง

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

}

ฉันต้องการส่งผ่านพารามิเตอร์ของตัวเองไปยังการโทรกลับรวมถึงผลลัพธ์ที่ได้จากการโทร AJAX


14
เป็นมูลค่าการกล่าวขวัญว่า jQuery.ajax () มีการตั้งค่าบริบทตั้งแต่ ver 1.4 ( jquery14.com/day-01/jquery-14 ) ดูตัวอย่างการใช้งานที่นี่: stackoverflow.com/questions/5097191/ajax-context- ตัวเลือก / …
russau

ฉันเสียใจที่มีปัญหาในการส่งคืนข้อมูลหลังจาก AJAX เสร็จสมบูรณ์แล้วฉันก็ทำอะไรบางอย่าง
Onaiggac

คำตอบ:


340

การแก้ปัญหาคือการเชื่อมโยงของตัวแปรผ่านการปิด


เป็นตัวอย่างพื้นฐานเพิ่มเติมนี่คือตัวอย่างฟังก์ชันที่รับและเรียกฟังก์ชัน callback รวมถึงตัวอย่างฟังก์ชัน callback:

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

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

สิ่งนี้เรียกการเรียกกลับและระบุอาร์กิวเมนต์เดี่ยว ตอนนี้คุณต้องการระบุอาร์กิวเมนต์เพิ่มเติมดังนั้นคุณจึงปิดการติดต่อกลับ

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

หรือให้ใช้ฟังก์ชั่นลูกศร ES6 ได้ง่ายขึ้น:

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

สำหรับตัวอย่างเฉพาะของคุณฉันไม่ได้ใช้.postฟังก์ชั่นใน jQuery แต่การสแกนอย่างรวดเร็วของเอกสารแนะนำว่าการโทรกลับควรเป็นตัวชี้ฟังก์ชั่นที่มีลายเซ็นต่อไปนี้:

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

ดังนั้นฉันคิดว่าวิธีแก้ปัญหามีดังนี้:

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

เกิดอะไรขึ้น?

ในบรรทัดสุดท้ายdoSomething(extraStuff)จะเรียกและผลของการภาวนาว่าเป็นตัวชี้ฟังก์ชัน

เพราะextraStuffถูกส่งผ่านเป็นอาร์กิวเมนต์doSomethingมันอยู่ภายในขอบเขตของdoSomethingฟังก์ชัน

เมื่อextraStuffมีการอ้างอิงในฟังก์ชั่นภายในที่ไม่ระบุชื่อกลับมาของdoSomethingมันถูกผูกไว้โดยปิดไปยังextraStuffอาร์กิวเมนต์ของฟังก์ชั่นด้านนอก สิ่งนี้เป็นจริงแม้หลังจากdoSomethingส่งคืนแล้ว

ฉันยังไม่ได้ทดสอบข้างต้น แต่ฉันได้เขียนรหัสที่คล้ายกันมากในช่วง 24 ชั่วโมงที่ผ่านมาและทำงานได้ตามที่ฉันอธิบาย

แน่นอนว่าคุณสามารถส่งผ่านตัวแปรหลายตัวแทนที่จะเป็นวัตถุ 'extraStuff' เดียวโดยขึ้นอยู่กับความชอบส่วนตัว / มาตรฐานการเข้ารหัสของคุณ


วัตถุประสงค์ของ 'var doSomething =' คืออะไร สิ่งนี้แตกต่างจากการประกาศ doSomething เป็นฟังก์ชั่นอย่างไร (เช่นฟังก์ชัน doSomething (... ) {})
Dave Neeley

7
ที่ไม่เป็นความจริง. กฎขอบเขตที่แตกต่างนำไปใช้กับการประกาศฟังก์ชันทั้งสองนี้ พฤติกรรมแตกต่างกันมากขึ้นอยู่กับ JS runtime (อ่านเบราว์เซอร์) ลองเปรียบเทียบชื่อฟังก์ชันที่แสดงใน stacktrace / breakpoint ใน Firebug
Ihor Kaharlichenko

1
Ihor: คุณถูกต้องอย่างแน่นอนเกี่ยวกับความแตกต่างระหว่างสองรูปแบบการประกาศ คำอธิบายที่ดีอยู่ที่นี่: stackoverflow.com/questions/336859/…
bradhouse

2
นี่อาจเป็นเรื่องยากที่จะติดตามสำหรับผู้ใช้ที่พยายามใช้สิ่งนี้กับปัญหาทั่วไปมากขึ้นเนื่องจากความเฉพาะเจาะจงของตัวอย่าง ฉันได้เพิ่มตัวอย่างแบบย่อทำให้มันง่ายขึ้นเล็กน้อยในการติดตามรวมถึงตัวอย่างฟังก์ชั่นลูกศรเพื่อป้องกันไม่ให้คำถามนี้ถูกปิดซ้ำซ้อนกับข้อเสนอที่เพิ่งโพสต์ใหม่ หากคุณรู้สึกว่าการแก้ไขของฉันเบี่ยงเบนไปจากความตั้งใจดั้งเดิมของคำตอบของคุณหรือไม่เหมาะสมอย่างยิ่งโปรดอย่าลังเลที่จะย้อนกลับ

4
ฉบับที่ 4 ของคำตอบนี้กำลังถูกกล่าวถึงใน meta
bjb568

82

เมื่อใช้doSomething(data, myDiv)คุณเรียกฟังก์ชั่นจริงและไม่ได้อ้างอิงถึงมัน

คุณสามารถส่งผ่านdoStomethingฟังก์ชันได้โดยตรง แต่คุณต้องแน่ใจว่ามีลายเซ็นที่ถูกต้อง

ถ้าคุณต้องการที่จะทำบางสิ่งบางอย่างในลักษณะที่เป็นอยู่คุณสามารถตัดการโทรในฟังก์ชั่นที่ไม่ระบุชื่อ

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

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

ภายในโค้ดฟังก์ชันที่ไม่ระบุชื่อคุณสามารถใช้ตัวแปรที่กำหนดไว้ในขอบเขตการปิดล้อม นี่คือวิธีการกำหนดขอบเขต Javascript


9
นี่คือทางออกที่ดีที่สุด IMO เพื่อความชัดเจน ไม่มีreturn function(...){...}ฟังก์ชั่นพิเศษภายในdoSomethingฟังก์ชั่น ฉันพบอีกตัวอย่างที่ชัดเจนของแนวคิดเดียวกันที่นี่: theelitist.net/ …
William Denniss

4
ฉันคิดว่ามีปัญหากับอันนี้ หากข้อมูลเพิ่มเติมของคุณ (myDiv ในกรณีนี้) เปลี่ยนแปลงก่อนที่การติดต่อกลับจะเริ่มขึ้นคุณจะได้รับค่าใหม่ไม่ใช่ข้อมูลเก่า! ในกรณีของฉันฉันได้ทำการปิด ajax call บนไอเท็มใน array และเมื่อถึงเวลาที่การเรียก () สำเร็จครั้งแรกก็คิดว่ามันสำเร็จในไอเท็มสุดท้ายเมื่อมันเป็นครั้งแรก! คำตอบของ bradhouse เหมาะกับฉัน
Chris

@Chris ใช่ตัวแปรที่จับได้สามารถเปลี่ยนได้ใน Javascript หากคุณไม่ต้องการพฤติกรรมนี้คุณต้องสร้างฟังก์ชั่นที่ชัดเจนเพื่อจับค่า สำนวนปกติมากที่สุดคือฟังก์ชั่นการดำเนินการด้วยตนเองในการจับภาพตัวแปร(function(myDiv){ ... })(myDiv) myDivสิ่งนี้จะกระทำโดยปริยายในคำตอบ @bradhouse
Vincent Robert

หากคุณเรียกใช้ ajax นี้ n ครั้งวิธีนี้จะสร้างฟังก์ชันที่แตกต่างกัน n ฟังก์ชันและส่งผ่านไปยังการเรียกกลับที่สำเร็จ นี่เป็นสิ่งที่ไม่ดีในแง่ของการปรนเปรอ เพื่อละเว้นคำตอบของ @zeroasterisk นี้ดี
yılmaz

ฉันเห็นด้วยกับ @WilliamDenniss, IMHO นี่เป็นวิธีแก้ปัญหาที่ง่ายและชัดเจนที่สุด
35990

52

จริงๆแล้วมันง่ายกว่าทุกคนทำให้เสียง ... โดยเฉพาะอย่างยิ่งถ้าคุณใช้$.ajax({})ไวยากรณ์พื้นฐานกับหนึ่งในฟังก์ชั่นตัวช่วย

เพียงผ่านkey: valueคู่เหมือนที่คุณทำกับวัตถุใด ๆ เมื่อคุณตั้งค่าคำขอ ajax ของคุณ ... (เพราะ$(this)ยังไม่ได้เปลี่ยนบริบทก็ยังคงเป็นทริกเกอร์สำหรับการโทรผูกข้างต้น)

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

หนึ่งในเหตุผลที่ดีกว่าการตั้งค่า var คือ var นั้นเป็นโกลบอลและเขียนทับ ... ถ้าคุณมี 2 สิ่งที่สามารถเรียกใช้ ajax ได้ในทางทฤษฎีคุณสามารถกระตุ้นพวกมันได้เร็วกว่าการตอบสนองของ ajax และ คุณต้องการให้ค่าสำหรับการโทรที่สองถูกส่งเข้ามาในครั้งแรก การใช้วิธีการด้านบนนี้จะไม่เกิดขึ้น (และมันก็ใช้ง่ายเช่นกัน)


3
นี่คือสิ่งที่ฉันกำลังมองหา ฉันไม่รู้ว่าเมื่อคุณระบุ dataType เป็น json มันจะแจงคำตอบให้คุณโดยอัตโนมัติ นั่นดี (:
ไก่

1
เสียงเหมือนวิธีที่ดีที่สุดในการส่ง params พิเศษเพื่อโทรกลับหาฉัน ฉันจะสนใจถ้าใครเห็นข้อเสียเปรียบด้วยวิธีนี้? ง่ายที่สุดและสง่างามที่สุด ขอบคุณ!
ม.ค. Zyka

" var คือโกลบอลและเขียนทับได้ " ไม่ใช่ถ้าคุณประกาศในฟังก์ชั่นซึ่งอ่านได้ง่ายกว่าโซลูชันด้านบน การใช้การปิดเป็นวิธีที่สะอาดกว่า - ดูคำตอบของฉันที่นี่: stackoverflow.com/a/18570951/885464
Lorenzo Polidori

5
@ LorenzoPolidori ฉันไม่เห็นด้วยฉันพบว่าวิธีการพารามิเตอร์เพิ่มเติมอ่านได้มากขึ้น
Andrew Plummer

2
อันนี้ยอดเยี่ยมอย่างแน่นอน ทำไมฉันไม่เคยเห็นสิ่งนี้มาก่อน ฉันไม่ต้องการclosureฉันแค่ต้องการให้มันทำงานและนี่คือเอซสะอาดเรียบง่ายและไม่ใช่โลก
Piotr Kula

21

ในโลกปัจจุบันมีคำตอบอื่นที่สะอาดกว่าและนำมาจากคำตอบ 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.
    ...
}

นี่คือคำถามและคำตอบเดิม: jQuery วิธีการ ?? ผ่านพารามิเตอร์เพิ่มเติมเพื่อโทรกลับสำเร็จสำหรับการโทร $ .ajax หรือไม่


8

คุณสามารถลองทำสิ่งต่อไปนี้:

function clicked() {

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

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

function doSomething(curData, curDiv) {

}

5

คุณสามารถปิด JavaScript:

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

และเมื่อใดที่คุณสามารถทำได้:

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

3

ฉันทำผิดพลาดในโพสต์ล่าสุดของฉัน นี่คือตัวอย่างการทำงานสำหรับวิธีส่งอาร์กิวเมนต์เพิ่มเติมในฟังก์ชันการเรียกกลับ:

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
คุณควรแก้ไขคำตอบเดิมหรือลบคำตอบก่อนที่จะเพิ่มคำตอบใหม่
Blazemonger

3

ไปง่าย ๆ ! :)

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

2

โซลูชันทั่วไปเพิ่มเติมสำหรับการส่งคำขอแบบอะซิงโครนัสโดยใช้.ajax()jQuery API และการปิดเพื่อส่งผ่านพารามิเตอร์เพิ่มเติมไปยังฟังก์ชันการเรียกกลับ:

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

สำหรับฉันและมือใหม่อื่น ๆ ที่เพิ่งติดต่อกับ Javascript
ฉันคิดว่าCloseure Solutionมันค่อนข้างสับสนเกินไป

ในขณะที่ฉันพบว่าคุณสามารถ easilly ผ่านพารามิเตอร์ได้มากเท่าที่คุณต้องการทุก ajax โทรกลับโดยใช้ jquery

นี่คือสองโซลูชั่นง่ายขึ้น

อันแรกซึ่ง@zeroasteriskได้กล่าวไว้ข้างต้นตัวอย่าง:

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

ประการที่สองผ่านการกำหนดข้อมูลด้วยตนเองโดยการเพิ่มลงในXHR object ที่มีอยู่ในช่วงชีวิต 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 );
            ...
        } 
    });
});

ในขณะที่คุณสามารถเห็นโซลูชั่นทั้งสองนี้มีข้อเสียเปรียบที่: คุณต้องเขียนโค้ดเพิ่มเติมเล็กน้อยทุกครั้งมากกว่าแค่เขียน:

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

อ่านเพิ่มเติมเกี่ยวกับ $ .ajax: http://api.jquery.com/jquery.ajax/


1

หากใครยังมาที่นี่นี่เป็นของฉัน:

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

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

thisจะเกิดอะไรขึ้นที่นี่หลังจากที่ผูกพันกับฟังก์ชั่นการโทรกลับก็จะสามารถใช้ได้ภายในฟังก์ชั่นเป็น

แนวคิดนี้มาจากวิธีที่ React ใช้bindฟังก์ชันการทำงาน


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

วิธีที่สอง:

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

จริงๆแล้วรหัสของคุณไม่ทำงานเพราะเมื่อคุณเขียน:

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

คุณวางสายงานเป็นพารามิเตอร์ที่สามมากกว่าอ้างอิงฟังก์ชั่น


0

ในฐานะที่เป็นภาคผนวกของคำตอบของ b01อาร์กิวเมนต์ที่สองของ$.proxyมักจะถูกใช้เพื่อรักษาthisข้อมูลอ้างอิง อาร์กิวเมนต์เพิ่มเติมที่ส่งไป$.proxyยังบางส่วนถูกนำไปใช้กับฟังก์ชั่นโดยเติมข้อมูลไว้ล่วงหน้า โปรดทราบว่าข้อโต้แย้งใด ๆ ที่$.postส่งไปยังการเรียกกลับจะถูกนำไปใช้ในตอนท้ายดังนั้นdoSomethingควรมีอาร์กิวเมนต์ที่ท้ายรายการอาร์กิวเมนต์:

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

วิธีการนี้ยังอนุญาตให้มีการเชื่อมโยงหลายอาร์กิวเมนต์กับการเรียกกลับ:

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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.