jQuery передает больше параметров в обратный вызов

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 () имеет настройку контекста, начиная с версии 1.4 ( jquery14.com/day-01/jquery-14 ). Пример его использования можно найти здесь: stackoverflow.com/questions/5097191/ajax-context- Вариант /…
Руссо
Я решил свою проблему с возвратом данных после завершения AJAX, а затем сделал что-то.
Onaiggac

Ответы:

340

Решением является привязка переменных через замыкание.


В качестве более простого примера, вот пример функции, которая получает и вызывает функцию обратного вызова, а также пример функции обратного вызова:

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 в зависимости от ваших личных предпочтений / стандартов кодирования.

bradhouse
источник
Какова цель 'var doSomething ='? Чем это отличается от простого объявления doSomething как функции (т.е. функции doSomething (...) {})
Дейв Нили,
7
Это не правда. К этим двум объявлениям функций применяются разные правила области видимости. Поведение сильно отличается в зависимости от времени выполнения JS (читайте в браузере). Также попробуйте сравнить имя функции, отображаемое в stacktrace / breakpoint в Firebug.
Игорь Кахарличенко
1
Игорь: Вы абсолютно правы относительно разницы между двумя стилями декларации. Хорошее объяснение здесь: stackoverflow.com/questions/336859/…
Bradhouse
2
Это может быть немного сложным для пользователей, пытающихся применить это к более общим проблемам из-за специфики примера. Я добавил упрощенный пример, которому немного легче следовать, а также пример функции стрелки, чтобы этот вопрос не закрывался как дубликат недавно опубликованного предложенного дубликата. Если вы чувствуете, что мои правки слишком сильно отклоняются от первоначального намерения вашего ответа или по-другому неуместны, пожалуйста, не стесняйтесь откатить их.
4
4-я редакция этого ответа обсуждается на meta
bjb568
82

При использовании doSomething(data, myDiv)вы фактически вызываете функцию и не ссылаетесь на нее.

Вы можете либо передать doStomethingфункцию напрямую, но вы должны убедиться, что она имеет правильную подпись.

Если вы хотите сохранить doSomething таким, какой он есть, вы можете обернуть его вызов в анонимную функцию.

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

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

Внутри кода анонимной функции вы можете использовать переменные, определенные в области видимости. Это способ работы Javascript.

Винсент Роберт
источник
9
Это лучшее решение ИМО, для наглядности. Никаких дополнительных return function(...){...}функций внутри doSomethingфункции. Я нашел еще один четкий пример того же понятия: theelitist.net/…
Уильям Денисс,
4
Я думаю, что есть проблема с этим. Если ваши дополнительные данные (в данном случае myDiv) изменятся до срабатывания обратного вызова, вы получите новое значение, а не старое! В моем случае я запускал ajax-вызовы для элементов в массиве, и к тому времени, когда был выполнен первый вызов success (), он думал, что он успешно завершился на последнем элементе, когда он был первым! Ответ Брэдхауса работал для меня.
Крис
@Chris Да, захваченные переменные могут быть изменены в Javascript. Если вы не хотите такого поведения, вам нужно создать явную функцию для захвата значения. Самая обычная идиома - это самозапускающаяся функция (function(myDiv){ ... })(myDiv)для захвата переменной myDiv. Это неявно сделано в ответе @bradhouse.
Винсент Роберт
Если вы вызываете этот ajax n раз, этот метод создает n различных функций и передает их в обратный вызов. Это плохо с точки зрения исполнения. Воздержаться от ответа @zeroasterisk - это хорошо.
Йылмаз
Я согласен с @WilliamDenniss, ИМХО это лучшее, простое и понятное решение
Себасира
52

На самом деле это проще, чем у всех, когда это звучит ... особенно если вы используете $.ajax({})базовый синтаксис против одной из вспомогательных функций.

Просто передайте key: valueпару, как вы бы делали для любого объекта, когда вы настраиваете свой ajax-запрос ... (поскольку $(this)контекст еще не изменился, он все еще является триггером для вызова bind выше)

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

Одна из причин, по которой это лучше, чем установка переменной, заключается в том, что переменная является глобальной и, следовательно, перезаписываемой ... если у вас есть две вещи, которые могут инициировать вызовы ajax, теоретически вы можете запускать их быстрее, чем отвечает вызов ajax, и у вас будет значение для второго вызова, переданного в первый. При использовании вышеописанного метода этого не произойдет (и его тоже довольно просто использовать).

zeroasterisk
источник
3
это именно то, что я искал. Я не знал, когда вы указали dataType как json, он автоматически проанализировал для вас ответ. Это приятно (:
Петух
1
Похоже, лучший способ передать дополнительные параметры для обратного вызова для меня. Мне было бы интересно, если кто-нибудь видит недостаток с таким подходом? Самый простой и самый элегантный. Спасибо!
Ян Зыка
msgstr " переменная глобальная и как таковая перезаписывается ". Это не так, если вы объявляете это внутри функции, которая также намного более читабельна, чем решение выше. Использование замыканий - более чистый подход - см. Мой ответ здесь: stackoverflow.com/a/18570951/885464
Лоренцо Полидори,
5
@LorenzoPolidori Я не согласен, я считаю, что подход с дополнительными параметрами гораздо более читабелен.
Эндрю Пламмер
2
Это абсолютно великолепно. Почему я никогда раньше этого не видел. Мне не нужно, closureмне просто нужно, чтобы это работало, и это абсолютно туз, чисто, просто и не глобально.
Петр Кула
21

В сегодняшнем мире есть другой ответ, который чище и взят из другого ответа переполнения стека:

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?

b01
источник
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
    }
});
molokoloco
источник
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это немного запутанно.

В то время как я обнаружил, что вы можете легко передать столько параметров, сколько захотите, для каждого обратного вызова 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функциональность.

Kőhalmy Zoltán
источник
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 --
}
Musa
источник
0

на самом деле, ваш код не работает, потому что когда вы пишете:

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

Вы помещаете вызов функции в качестве третьего параметра, а не ссылки на функцию .

jrharshath
источник
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;
}
hyperslug
источник