JavaScript: Passando parâmetros para uma função de retorno de chamada

289

Estou tentando passar algum parâmetro para uma função usada como retorno de chamada, como posso fazer isso?

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

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

callbackTester (tryMe, "hello", "goodbye");
vitto
fonte
9
O que você está fazendo deve funcionar. Que problemas você tem?
precisa
1
Seu código funciona bem, qual é o problema?
Sarfraz
1
Ele deve funcionar ... jsfiddle.net/QXQZj
Hristo
pena que foi minha culpa sobre a sintaxe de código principal, eu achava que era isso, porque esta é a primeira vez que eu usar um callback em JavaScript
vitto
Se você deseja adicionar parâmetros a um retorno de chamada, mas não pode alterar o que está chamando (como você não tem poder para alterar a ordem dos argumentos, é possível pré-vincular alguns dos parâmetros de retorno de chamada com JS bind, como mostrei em esta resposta: stackoverflow.com/a/28120741/1695680
ThorSummoner

Respostas:

253

Se você quiser algo um pouco mais geral, use a variável argumentos da seguinte maneira:

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

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

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

Mas, caso contrário, seu exemplo funciona bem (argumentos [0] podem ser usados ​​no lugar do retorno de chamada no testador)

Simon Scarfe
fonte
53
Enquanto estivermos no espírito de ser geral, callback.apply(arguments)o corpo da função callbackTesterfor extensível além do cenário de dois argumentos.
11118 Steven
1
desculpe, foi um erro de sintaxe no código principal, pensei que fosse porque é a primeira vez que uso um retorno de chamada em JavaScript, você me ajudou a entender que não era o problema e a ver um ótimo exemplo.
vitto
3
Para sua informação, usar uma função anônima (resposta de Marimuthu) ou .bind () (resposta de Andy) são maneiras muito mais limpas de passar argumentos para um retorno de chamada.
Antoine
203

Isso também funcionaria:

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

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

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

Outro cenário:

// 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); 
}); 
Marimuthu Madasamy
fonte
2
Isso funciona muito bem porque também permite que a função anônima passe parâmetros como: callbackTester (function (data) {tryMe (data, "hello", "goodbye");});
Michael Khalili
Também gosto de verificar se o retorno de chamada é, de fato, uma função. if (typeof window[callback] == 'function') window[callback].call(this);
GreeKatrina 20/05
63

Sua pergunta não está clara. Se você está perguntando como fazer isso de uma maneira mais simples, consulte o método .bind () da 5ª edição do ECMAScript , que é membro do Function.prototype . Usando-o, você pode fazer algo assim:

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

function callbackTester (callback) {
    callback();
}

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

Você também pode usar o código a seguir, que adiciona o método se ele não estiver disponível no navegador atual:

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

Exemplo

bind () - PrototypeJS Documentation

Andy E
fonte
Fora de interesse, qual é a diferença entre Array.prototype.slice.call(arguments)e arguments.slice()?
sje397
7
@ sje397: argumentos não é uma matriz * real *, portanto, não possui um método slice () . No entanto, o método slice () no Array.prototype é intencionalmente genérico, para que você possa transmitir qualquer objeto que possua índices numéricos e uma propriedade length , e funcionará.
Andy E
2
Este é o mais elegante resposta
Antoine
Esse .bind () é realmente ótimo e amplia muito o uso e a simplicidade dos retornos de chamada. Como uma amostra básica para entendê-lo, se você tiver: #f = function(arg1,arg2){alert(arg1+arg2);}.bind(this,"abc"); f("def") // Gives "abcdef"
Le Droid
Esta é realmente uma resposta grt. Impressionante e funciona bem para mim. Obrigado :)
Vishnu Mishra
13

Quando você tem um retorno de chamada que será chamado por algo diferente do seu código com um número específico de parâmetros e deseja passar parâmetros adicionais, pode passar uma função de wrapper como o retorno de chamada e dentro do wrapper passar os parâmetros adicionais.

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
}
Blake Mills
fonte
9

Se você não tiver certeza de quantos parâmetros serão passados ​​para as funções de retorno de chamada, use a applyfunção

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

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

callbackTester(tryMe,['hello','goodbye']);
Zeeman Chen
fonte
4

Agrupe as funções 'filho' que estão sendo passadas como / com argumentos nos wrappers de função para impedir que sejam avaliados quando a função 'pai' for chamada.

function outcome(){
    return false;
}

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

process(function(){alert("OKAY");},function(){alert("OOPS");})
Alan McCune
fonte
4

Código de uma pergunta com qualquer número de parâmetros e um contexto de retorno de chamada:

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
Adam Skobodzinski
fonte
2

Use a função ao curry como neste exemplo simples.

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>


fonte
0

Uma nova versão para o cenário em que o retorno de chamada será chamado por alguma outra função, não pelo seu próprio código, e você deseja adicionar parâmetros adicionais.

Por exemplo, vamos fingir que você tem muitas chamadas aninhadas com retornos de sucesso e erro. Usarei promessas angulares para este exemplo, mas qualquer código javascript com retornos de chamada seria o mesmo para esse propósito.

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

Agora você pode organizar seu código organizando uma função para registrar erros, mantendo a origem do erro para fins de depuração. É assim que você deve refatorar seu código:

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

A função de chamada ainda adicionará o parâmetro de erro após os parâmetros da função de retorno de chamada.

Juangui Jordán
fonte
0

Eu estava procurando a mesma coisa e acabei com a solução e aqui está um exemplo simples, se alguém quiser passar por isso.

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

Também postado na outra pergunta aqui

Code_Crash
fonte
0

Deixe-me dar um exemplo muito simples de estilo do Node.js. usando um retorno de chamada:

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

e o HTML que renderizará o resultado:

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

Você pode brincar com ele aqui: https://jsfiddle.net/q8gnvcts/ - por exemplo, tente passar a string em vez do número: myTest ('alguma string', 6, função (err, resultado) .. e veja o resultado.

Espero que este exemplo ajude, porque representa a idéia muito básica das funções de retorno de chamada.

Vlado
fonte
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");

leia mais sobre a sintaxe de propagação

Dmitry Grinko
fonte
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
fonte
1
Por favor, adicione uma explicação sobre o que você está fazendo e por que :)
Preston Badeer
Ok, eu vou fazer a partir da minha próxima resposta, desculpe pelo exposto, porque esta é a minha primeira resposta.
Santhos Jery 04/11/19