Créer un rappel personnalisé en JavaScript


322

Tout ce que je dois faire est d'exécuter une fonction de rappel lorsque l'exécution de ma fonction actuelle se termine.

function LoadData() 
{
    alert('The data has been loaded');
    //Call my callback with parameters. For example,
    //callback(loadedData , currentObject);
}

Un consommateur pour cette fonction devrait ressembler à ceci:

object.LoadData(success);

function success(loadedData , currentObject) 
{
  //Todo: some action here 
}

Comment puis-je implémenter cela?


3
object.LoadData(success)l'appel doit être après function success est défini. Sinon, vous obtiendrez une erreur vous indiquant que la fonction n'est pas définie.
J. Bruni

Réponses:


574

En fait, votre code fonctionnera à peu près tel quel, déclarez simplement votre rappel comme argument et vous pouvez l'appeler directement en utilisant le nom de l'argument.

Les bases

function doSomething(callback) {
    // ...

    // Call the callback
    callback('stuff', 'goes', 'here');
}

function foo(a, b, c) {
    // I'm the callback
    alert(a + " " + b + " " + c);
}

doSomething(foo);

Cela appellera doSomething, qui appellera foo, qui alertera "les choses vont ici".

Notez qu'il est très important de passer la fonction reference ( foo), plutôt que d'appeler la fonction et de passer son résultat ( foo()). Dans votre question, vous le faites correctement, mais cela vaut la peine d'être souligné car c'est une erreur courante.

Des trucs plus avancés

Parfois, vous souhaitez appeler le rappel pour qu'il voit une valeur spécifique pour this. Vous pouvez facilement le faire avec la callfonction JavaScript :

function Thing(name) {
    this.name = name;
}
Thing.prototype.doSomething = function(callback) {
    // Call our callback, but using our own instance as the context
    callback.call(this);
}

function foo() {
    alert(this.name);
}

var t = new Thing('Joe');
t.doSomething(foo);  // Alerts "Joe" via `foo`

Vous pouvez également passer des arguments:

function Thing(name) {
    this.name = name;
}
Thing.prototype.doSomething = function(callback, salutation) {
    // Call our callback, but using our own instance as the context
    callback.call(this, salutation);
}

function foo(salutation) {
    alert(salutation + " " + this.name);
}

var t = new Thing('Joe');
t.doSomething(foo, 'Hi');  // Alerts "Hi Joe" via `foo`

Parfois, il est utile de passer les arguments que vous souhaitez donner au rappel sous forme de tableau, plutôt qu'individuellement. Vous pouvez utiliser applypour cela:

function Thing(name) {
    this.name = name;
}
Thing.prototype.doSomething = function(callback) {
    // Call our callback, but using our own instance as the context
    callback.apply(this, ['Hi', 3, 2, 1]);
}

function foo(salutation, three, two, one) {
    alert(salutation + " " + this.name + " - " + three + " " + two + " " + one);
}

var t = new Thing('Joe');
t.doSomething(foo);  // Alerts "Hi Joe - 3 2 1" via `foo`

Je sais que cela fonctionnera si je n'ai pas de paramètres comme l'exemple que vous avez écrit, mais quand j'essaie de passer une fonction avec des paramètres, il lance une exception et me dit que la fonction n'est pas définie
Amgad Fahmi

@TiTaN: C'est étrange, il n'y a rien de spécial à passer des paramètres dans le rappel. La référence de rappel que vous passez dans votre fonction est une référence de fonction comme les autres, vous pouvez faire toutes les choses normales avec.
TJ Crowder

4
@everyone qui a répondu: Je pense que le problème de TiTaN est qu'il ne sait pas comment passer une fonction qui nécessite des arguments dans un rappel qui ne passe aucun argument. Pensez setTimeout(). La réponse est d'envelopper le rappel dans une clôture:doSomething(function(){foo('this','should','work')})
slebetman

Quelqu'un pointe TiTaN vers un fil (de préférence sur SO) discutant du problème ci-dessus, ma recherche-fu est faible aujourd'hui.
slebetman

1
@Webwoman - Cela dépend de votre cas d'utilisation. Vous pouvez le passer comme argument, ou l'inclure dans une sorte d'objet settings / options, ou dans plusieurs autres options.
TJ Crowder

78

Il est recommandé de vous assurer que le rappel est une fonction réelle avant de tenter de l'exécuter:

if (callback && typeof(callback) === "function") {

  callback();
}

21
if(typeof callback == "function")aura le même résultat.
2013

22
Oui, mais s'il n'y a pas de rappel, pourquoi s'embêter à le taper? C'est le point de callback && ...
theonlygusti

61

Mon 2 cent. Pareil mais différent...

<script>
    dosomething("blaha", function(){
        alert("Yay just like jQuery callbacks!");
    });


    function dosomething(damsg, callback){
        alert(damsg);
        if(typeof callback == "function") 
        callback();
    }
</script>

7
J'adore cet extrait, je le cherchais
vimal1083

10
function loadData(callback) {

    //execute other requirement

    if(callback && typeof callback == "function"){
        callback();
   }
}

loadData(function(){

   //execute callback

});

6
Veuillez envisager de modifier votre message pour ajouter plus d'explications sur ce que fait votre code et pourquoi il résoudra le problème. Une réponse qui contient principalement du code (même si cela fonctionne) n'aidera généralement pas l'OP à comprendre leur problème. Cependant, dans ce cas, il s'agit d'une très vieille question avec des réponses très appréciées déjà publiées, cela ne vaut peut-être pas la peine d'y répondre quand il y a des questions plus récentes qui pourraient faire plus d'attention.
SuperBiatedMan

1
J'aime cette réponse à sa démonstration avancée de ce que les gens veulent voir.
Aft3rL1f3

5
   function callback(e){
      return e;
   }
    var MyClass = {
       method: function(args, callback){
          console.log(args);
          if(typeof callback == "function")
          callback();
       }    
    }

==============================================

MyClass.method("hello",function(){
    console.log("world !");
});

==============================================

Le résultat est:

hello world !

4

Si vous souhaitez exécuter une fonction lorsque quelque chose est fait. L'une des bonnes solutions consiste à écouter les événements. Par exemple, je vais implémenter un Dispatcher, une DispatcherEventclasse avec ES6, puis:

let Notification = new Dispatcher()
Notification.on('Load data success', loadSuccessCallback)

const loadSuccessCallback = (data) =>{
   ...
}
//trigger a event whenever you got data by
Notification.dispatch('Load data success')

Répartiteur:

class Dispatcher{
  constructor(){
    this.events = {}
  }

  dispatch(eventName, data){
    const event = this.events[eventName]
    if(event){
      event.fire(data)
    }
  }

  //start listen event
  on(eventName, callback){
    let event = this.events[eventName]
    if(!event){
      event = new DispatcherEvent(eventName)
      this.events[eventName] = event
    }
    event.registerCallback(callback)
  }

  //stop listen event
  off(eventName, callback){
    const event = this.events[eventName]
    if(event){
      delete this.events[eventName]
    }
  }
}

DispatcherEvent:

class DispatcherEvent{
  constructor(eventName){
    this.eventName = eventName
    this.callbacks = []
  }

  registerCallback(callback){
    this.callbacks.push(callback)
  }

  fire(data){
    this.callbacks.forEach((callback=>{
      callback(data)
    }))
  }
}

Bon codage!

p / s: Mon code manque gérer certaines exceptions d'erreur


1
function LoadData(callback) 
{
    alert('the data have been loaded');
    callback(loadedData, currentObject);
}

1

Lors de l'appel de la fonction de rappel, nous pourrions l'utiliser comme ci-dessous:

consumingFunction(callbackFunctionName)

Exemple:

// Callback function only know the action,
// but don't know what's the data.
function callbackFunction(unknown) {
  console.log(unknown);
}

// This is a consuming function.
function getInfo(thenCallback) {
  // When we define the function we only know the data but not
  // the action. The action will be deferred until excecuting.
  var info = 'I know now';
  if (typeof thenCallback === 'function') {
    thenCallback(info);    
  }
}

// Start.
getInfo(callbackFunction); // I know now

Ceci est le Codepend avec un exemple complet.


1

Certaines des réponses, bien que correctes, peuvent être un peu difficiles à comprendre. Voici un exemple en termes simples:

var users = ["Sam", "Ellie", "Bernie"];

function addUser(username, callback)
{
    setTimeout(function()
    {
        users.push(username);
        callback();
    }, 200);
}

function getUsers()
{
    setTimeout(function()
    {
        console.log(users);
    }, 100);
}

addUser("Jake", getUsers);

Le rappel signifie que "Jake" est toujours ajouté aux utilisateurs avant d'afficher la liste des utilisateurs avec console.log.

Source (YouTube)


0

Essayer:

function LoadData (callback)
{
    // ... Process whatever data
    callback (loadedData, currentObject);
}

Les fonctions sont de première classe en JavaScript ; vous pouvez simplement les faire circuler.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.