JavaScript: передача параметров в функцию обратного вызова

290

Я пытаюсь передать какой-то параметр в функцию, используемую в качестве обратного вызова, как я могу это сделать?

function tryMe (param1, param2) {
    alert (param1 + " and " + param2);
}

function callbackTester (callback, param1, param2) {
    callback (param1, param2);
}

callbackTester (tryMe, "hello", "goodbye");
Vitto
источник
9
То, что вы делаете, должно работать. Какие проблемы у тебя?
Даниэль Вассалло
1
Ваш код работает нормально, в чем проблема?
Сарфраз
1
Это должно работать ... jsfiddle.net/QXQZj
Христо
извините, это была моя ошибка в синтаксисе основного кода, я думал, что это так, потому что это первый раз, когда я использую обратный вызов в JavaScript
vitto
Если вы хотите добавить параметры к обратному вызову, но не можете изменить то, что его вызывает (поскольку у вас нет полномочий изменять порядок аргументов, вы можете предварительно связать некоторые параметры обратных вызовов с помощью JS bind, как я показал на этот ответ: stackoverflow.com/a/28120741/1695680
ThorSummoner

Ответы:

253

Если вы хотите что-то более общее, вы можете использовать переменную arguments следующим образом:

function tryMe (param1, param2) {
    alert(param1 + " and " + param2);
}

function callbackTester (callback) {
    callback (arguments[1], arguments[2]);
}

callbackTester (tryMe, "hello", "goodbye");

Но в остальном ваш пример работает нормально (аргументы [0] могут использоваться вместо обратного вызова в тестере)

Саймон Скарф
источник
53
Пока мы находимся в духе универсальности, callback.apply(arguments)тело функции for callbackTesterрасширяется за пределы сценария с двумя аргументами.
Стивен
1
извините, это была синтаксическая ошибка в основном коде, я думал, что это так, потому что я впервые использую обратный вызов в JavaScript, вы помогли мне понять, что это не проблема, и увидеть отличный пример.
Витто
3
К вашему сведению, использование анонимной функции (ответ Маримуту) или .bind () (ответ Энди) - намного более чистые способы передачи аргументов в обратный вызов.
Антуан
203

Это также будет работать:

// callback function
function tryMe (param1, param2) { 
    alert (param1 + " and " + param2); 
} 

// callback executer 
function callbackTester (callback) { 
    callback(); 
} 

// test function
callbackTester (function() {
    tryMe("hello", "goodbye"); 
}); 

Еще один сценарий:

// callback function
function tryMe (param1, param2, param3) { 
    alert (param1 + " and " + param2 + " " + param3); 
} 

// callback executer 
function callbackTester (callback) { 
//this is the more obivous scenario as we use callback function
//only when we have some missing value
//get this data from ajax or compute
var extraParam = "this data was missing" ;

//call the callback when we have the data
    callback(extraParam); 
} 

// test function
callbackTester (function(k) {
    tryMe("hello", "goodbye", k); 
}); 
Маримуту Мадасами
источник
2
Это прекрасно работает, потому что также позволяет анонимной функции передавать параметры следующим образом: callbackTester (function (data) {tryMe (data, "hello", "goodbye");});
Майкл Халили
Мне также нравится проверять, что обратный вызов, по сути, является функцией. if (typeof window[callback] == 'function') window[callback].call(this);
GreeKatrina
63

Ваш вопрос неясен. Если вы спрашиваете, как вы можете сделать это более простым способом, вам следует взглянуть на метод ECMAScript 5-го издания .bind () , который является членом Function.prototype . Используя его, вы можете сделать что-то вроде этого:

function tryMe (param1, param2) {
    alert (param1 + " and " + param2);
}

function callbackTester (callback) {
    callback();
}

callbackTester(tryMe.bind(null, "hello", "goodbye"));

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

// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
  Function.prototype.bind = function(){ 
    var fn = this, args = Array.prototype.slice.call(arguments),
        object = args.shift(); 
    return function(){ 
      return fn.apply(object, 
        args.concat(Array.prototype.slice.call(arguments))); 
    }; 
  };
}

пример

bind () - Документация по прототипу JS

Энди Э
источник
Из интереса, в чем разница между Array.prototype.slice.call(arguments)и arguments.slice()?
sje397
7
@ sje397: аргументы не являются * реальными * массивами, поэтому у них нет метода slice () . Однако метод slice () в Array.prototype намеренно универсален, поэтому вы можете передать любой объект, имеющий числовые индексы и свойство length, и он будет работать.
Энди Э
2
Это самый элегантный ответ
Антуан
Этот .bind () действительно хорош и значительно расширяет возможности использования и простоты обратных вызовов. В качестве основного примера, чтобы понять это, если у вас есть:f = function(arg1,arg2){alert(arg1+arg2);}.bind(this,"abc"); f("def") // Gives "abcdef"
Le Droid
Это действительно точный ответ. Потрясающе и хорошо работает для меня. Спасибо :)
Вишну Мишра
13

Если у вас есть обратный вызов, который будет вызываться чем-то отличным от вашего кода с определенным количеством параметров, и вы хотите передать дополнительные параметры, вы можете передать функцию-оболочку в качестве обратного вызова, а внутри оболочки передать дополнительные параметры.

function login(accessedViaPopup) {
    //pass FB.login a call back function wrapper that will accept the
    //response param and then call my "real" callback with the additional param
    FB.login(function(response){
        fb_login_callback(response,accessedViaPopup);
    });
}

//handles respone from fb login call
function fb_login_callback(response, accessedViaPopup) {
    //do stuff
}
Блейк Миллс
источник
9

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

function tryMe (param1, param2) {
  alert (param1 + " and " + param2);
}

function callbackTester(callback,params){
    callback.apply(this,params);
}

callbackTester(tryMe,['hello','goodbye']);
Зееман Чен
источник
4

Оберните 'дочернюю' функцию (и), передаваемую как / с аргументами в оболочках функций, чтобы предотвратить их оценку при вызове 'родительской' функции.

function outcome(){
    return false;
}

function process(callbackSuccess, callbackFailure){
    if ( outcome() )
        callbackSuccess();
    else
        callbackFailure();
}

process(function(){alert("OKAY");},function(){alert("OOPS");})
Алан Маккун
источник
4

Код из вопроса с любым количеством параметров и контекстом обратного вызова:

function SomeFunction(name) {
    this.name = name;
}
function tryMe(param1, param2) {
    console.log(this.name + ":  " + param1 + " and " + param2);
}
function tryMeMore(param1, param2, param3) {
    console.log(this.name + ": " + param1 + " and " + param2 + " and even " + param3);
}
function callbackTester(callback, callbackContext) {
    callback.apply(callbackContext, Array.prototype.splice.call(arguments, 2));
}
callbackTester(tryMe, new SomeFunction("context1"), "hello", "goodbye");
callbackTester(tryMeMore, new SomeFunction("context2"), "hello", "goodbye", "hasta la vista");

// context1: hello and goodbye
// context2: hello and goodbye and even hasta la vista
Адам Скободзински
источник
2

Используйте функцию карри, как в этом простом примере.

const BTN = document.querySelector('button')
const RES = document.querySelector('p')

const changeText = newText => () => {
  RES.textContent = newText
}

BTN.addEventListener('click', changeText('Clicked!'))
<button>ClickMe</button>
<p>Not clicked<p>


источник
0

Новая версия для сценария, где обратный вызов будет вызываться какой-то другой функцией, а не вашим собственным кодом, и вы хотите добавить дополнительные параметры.

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

someObject.doSomething(param1, function(result1) {
  console.log("Got result from doSomething: " + result1);
  result.doSomethingElse(param2, function(result2) {
    console.log("Got result from doSomethingElse: " + result2);
  }, function(error2) {
    console.log("Got error from doSomethingElse: " + error2);
  });
}, function(error1) {
  console.log("Got error from doSomething: " + error1);
});

Теперь вы можете снять загромождение своего кода, определив функцию для регистрации ошибок, сохраняя источник ошибки для целей отладки. Вот как вы бы приступили к рефакторингу вашего кода:

someObject.doSomething(param1, function (result1) {
  console.log("Got result from doSomething: " + result1);
  result.doSomethingElse(param2, function (result2) {
    console.log("Got result from doSomethingElse: " + result2);
  }, handleError.bind(null, "doSomethingElse"));
}, handleError.bind(null, "doSomething"));

/*
 * Log errors, capturing the error of a callback and prepending an id
 */
var handleError = function (id, error) {
  var id = id || "";
  console.log("Got error from " + id + ": " + error);
};

Вызывающая функция все равно добавит параметр ошибки после параметров вашей функции обратного вызова.

Хуанги Джордан
источник
0

Я искал то же самое и в итоге нашел решение, и вот простой пример, если кто-то хочет пройти через это.

var FA = function(data){
   console.log("IN A:"+data)
   FC(data,"LastName");
};
var FC = function(data,d2){
   console.log("IN C:"+data,d2)
};
var FB = function(data){
   console.log("IN B:"+data);
    FA(data)
};
FB('FirstName')

Также размещен на другой вопрос здесь

Code_Crash
источник
0

Позвольте мне привести очень простой пример использования обратного вызова в стиле Node.js:

/**
 * Function expects these arguments: 
 * 2 numbers and a callback function(err, result)
 */
var myTest = function(arg1, arg2, callback) {
  if (typeof arg1 !== "number") {
    return callback('Arg 1 is not a number!', null); // Args: 1)Error, 2)No result
  }
  if (typeof arg2 !== "number") {
    return callback('Arg 2 is not a number!', null); // Args: 1)Error, 2)No result
  }
  if (arg1 === arg2) {
    // Do somethign complex here..
    callback(null, 'Actions ended, arg1 was equal to arg2'); // Args: 1)No error, 2)Result
  } else if (arg1 > arg2) {
    // Do somethign complex here..
    callback(null, 'Actions ended, arg1 was > from arg2'); // Args: 1)No error, 2)Result
  } else {
    // Do somethign else complex here..
    callback(null, 'Actions ended, arg1 was < from arg2'); // Args: 1)No error, 2)Result
  }
};


/**
 * Call it this way: 
 * Third argument is an anonymous function with 2 args for error and result
 */
myTest(3, 6, function(err, result) {
  var resultElement = document.getElementById("my_result");
  if (err) {
    resultElement.innerHTML = 'Error! ' + err;
    resultElement.style.color = "red";
    //throw err; // if you want
  } else {
    resultElement.innerHTML = 'Result: ' + result;
    resultElement.style.color = "green";
  }
});

и HTML-код, который будет отображать результат:

<div id="my_result">
  Result will come here!
</div>

Вы можете поиграть с этим здесь: https://jsfiddle.net/q8gnvcts/ - например, попробуйте передать строку вместо числа: myTest ('some string', 6, function (err, result) .. и посмотрите результат.

Я надеюсь, что этот пример помогает, потому что он представляет очень основную идею функций обратного вызова.

Владо
источник
0
function tryMe(param1, param2) {
  console.log(param1 + " and " + param2);
}

function tryMe2(param1) {
  console.log(param1);
}

function callbackTester(callback, ...params) {
  callback(...params);
}



callbackTester(tryMe, "hello", "goodbye");

callbackTester(tryMe2, "hello");

узнать больше о синтаксисе распространения

Дмитрий Гринько
источник
0
//Suppose function not taking any parameter means just add the GetAlterConfirmation(function(result) {});
GetAlterConfirmation('test','messageText',function(result) {
                        alert(result);
    }); //Function into document load or any other click event.


function GetAlterConfirmation(titleText, messageText, _callback){
         bootbox.confirm({
                    title: titleText,
                    message: messageText,
                    buttons: {
                        cancel: {
                            label: '<i class="fa fa-times"></i> Cancel'
                        },
                        confirm: {
                            label: '<i class="fa fa-check"></i> Confirm'
                        }
                    },
                    callback: function (result) {
                        return _callback(result); 
                    }
                });
Santhos Jery
источник
1
Пожалуйста, добавьте объяснение того, что вы делаете и почему :)
Престон Бадир
Хорошо, я сделаю из моего следующего ответа, извините за вышесказанное, потому что это мой первый ответ.
Santhos Jery