JavaScript: passage de paramètres à une fonction de rappel

289

J'essaie de passer un paramètre à une fonction utilisée comme rappel, comment faire?

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

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

callbackTester (tryMe, "hello", "goodbye");
vitto
la source
9
Ce que vous faites devrait fonctionner. Quels problèmes avez-vous?
Daniel Vassallo
1
Votre code fonctionne bien, quel est le problème?
Sarfraz
1
Ça devrait marcher ... jsfiddle.net/QXQZj
Hristo
désolé c'était ma faute sur la syntaxe du code principal, je pensais que c'était parce que c'est la première fois que j'utilise un rappel en JavaScript
vitto
Si vous voulez ajouter des paramètres à un rappel mais ne pouvez pas changer son appel (comme vous n'avez pas le pouvoir de changer l'ordre des arguments, vous pouvez pré-lier certains des paramètres de rappel avec la liaison JS, comme je l'ai montré sur cette réponse: stackoverflow.com/a/28120741/1695680
ThorSummoner

Réponses:

253

Si vous voulez quelque chose d'un peu plus général, vous pouvez utiliser la variable arguments comme ceci:

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

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

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

Mais sinon, votre exemple fonctionne bien (les arguments [0] peuvent être utilisés à la place du rappel dans le testeur)

Simon Scarfe
la source
53
Tant que nous sommes dans l'esprit d'être général, callback.apply(arguments)le corps de la fonction callbackTesterest extensible au-delà du scénario à deux arguments.
Steven
1
désolé, c'était une erreur de syntaxe dans le code principal, je pensais que c'était parce que c'est la première fois que j'utilise un rappel en JavaScript, vous m'avez aidé à comprendre que ce n'était pas le problème, et à voir un excellent exemple.
vitto
3
Pour info, utiliser une fonction anonyme (réponse de Marimuthu) ou .bind () (réponse d'Andy) sont des moyens beaucoup plus propres de passer des arguments à un rappel.
antoine
203

Cela fonctionnerait également:

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

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

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

Un autre scénario:

// 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
la source
2
Cela fonctionne très bien car il permet également à la fonction anonyme de transmettre des paramètres comme celui-ci: callbackTester (function (data) {tryMe (data, "hello", "goodbye");});
Michael Khalili
J'aime aussi vérifier que le rappel est bien une fonction. if (typeof window[callback] == 'function') window[callback].call(this);
GreeKatrina
63

Votre question n'est pas claire. Si vous demandez comment vous pouvez le faire d'une manière plus simple, vous devriez jeter un œil à la méthode ECMAScript 5e édition .bind () , qui est membre de Function.prototype . En l'utilisant, vous pouvez faire quelque chose comme ceci:

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

function callbackTester (callback) {
    callback();
}

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

Vous pouvez également utiliser le code suivant, qui ajoute la méthode si elle n'est pas disponible dans le navigateur actuel:

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

Exemple

bind () - Documentation PrototypeJS

Andy E
la source
Par intérêt, quelle est la différence entre Array.prototype.slice.call(arguments)et arguments.slice()?
sje397
7
@ sje397: les arguments ne sont pas un tableau * real *, donc il n'a pas de méthode slice () . Cependant, la méthode slice () sur le Array.prototype est intentionnellement générique, vous pouvez donc passer n'importe quel objet qui a des index numériques et une propriété de longueur et cela fonctionnera.
Andy E
2
Ceci est la réponse la plus élégante
antoine
Ce .bind () est vraiment génial et étend beaucoup l'utilisation et la simplicité des rappels. Comme exemple de base pour le comprendre, si vous avez:f = function(arg1,arg2){alert(arg1+arg2);}.bind(this,"abc"); f("def") // Gives "abcdef"
Le Droid
C'est vraiment une bonne réponse. Génial et fonctionne bien pour moi. Merci :)
Vishnu Mishra
13

Lorsque vous avez un rappel qui sera appelé par quelque chose d'autre que votre code avec un nombre spécifique de paramètres et que vous souhaitez passer des paramètres supplémentaires, vous pouvez passer une fonction wrapper comme rappel et à l'intérieur du wrapper passer le ou les paramètres supplémentaires.

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
la source
9

Si vous n'êtes pas sûr du nombre de paramètres que vous allez passer dans les fonctions de rappel, utilisez applyfunction.

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

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

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

Enveloppez la (les) fonction (s) 'enfant (s) en cours de passage sous / avec des arguments dans les wrappers de fonction pour éviter qu'elles ne soient évaluées lorsque la fonction' parent 'est appelée.

function outcome(){
    return false;
}

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

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

Code d'une question avec un nombre quelconque de paramètres et un contexte de rappel:

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
la source
2

Utilisez la fonction curry comme dans cet exemple simple.

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>


la source
0

Une nouvelle version pour le scénario où le rappel sera appelé par une autre fonction, pas votre propre code, et vous souhaitez ajouter des paramètres supplémentaires.

Par exemple, supposons que vous ayez beaucoup d'appels imbriqués avec succès et rappels d'erreur. J'utiliserai des promesses angulaires pour cet exemple mais tout code javascript avec des rappels serait le même à cet effet.

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

Vous pouvez maintenant désencombrer votre code en définissant une fonction pour consigner les erreurs, en conservant l'origine de l'erreur à des fins de débogage. Voici comment procéder pour refactoriser votre code:

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

La fonction appelante ajoutera toujours le paramètre d'erreur après les paramètres de votre fonction de rappel.

Juangui Jordán
la source
0

Je cherchais la même chose et me retrouvais avec la solution et ici c'est un exemple simple si quelqu'un veut passer par là.

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

Également affiché sur l'autre question ici

Code_Crash
la source
0

Permettez-moi de vous donner un exemple très simple de style Node.js d'utilisation d'un rappel:

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

et le HTML qui rendra le résultat:

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

Vous pouvez jouer avec ici: https://jsfiddle.net/q8gnvcts/ - par exemple, essayez de passer une chaîne au lieu d'un nombre: myTest ('some string', 6, function (err, result) .. et voyez le résultat.

J'espère que cet exemple aide car il représente l'idée très basique des fonctions de rappel.

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

en savoir plus sur la syntaxe de propagation

Dmitry Grinko
la source
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
la source
1
Veuillez ajouter une explication de ce que vous faites et pourquoi :)
Preston Badeer
D'accord, je ferai de ma prochaine réponse, désolé pour ce qui précède parce que c'est ma première réponse.
Santhos Jery