Crea un callback personalizzato in JavaScript


322

Tutto quello che devo fare è eseguire una funzione di callback al termine della mia attuale esecuzione della funzione.

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

Un consumatore per questa funzione dovrebbe essere così:

object.LoadData(success);

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

Come posso implementarlo?


3
object.LoadData(success)la chiamata deve essere dopo aver function success definito. In caso contrario, verrà visualizzato un errore che indica che la funzione non è definita.
J. Bruni,

Risposte:


574

In realtà, il tuo codice funzionerà praticamente così com'è, basta dichiarare il callback come argomento e puoi chiamarlo direttamente usando il nome dell'argomento.

Le basi

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

Che chiamerà doSomething, che chiamerà foo, che avviserà "la roba va qui".

Si noti che è molto importante passare il riferimento alla funzione ( foo), piuttosto che chiamare la funzione e passare il suo risultato ( foo()). Nella tua domanda, lo fai correttamente, ma vale la pena sottolineare perché è un errore comune.

Roba più avanzata

A volte vuoi chiamare il callback in modo che veda un valore specifico per this. Puoi farlo facilmente con la callfunzione 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`

Puoi anche passare argomenti:

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`

A volte è utile passare gli argomenti che si desidera dare al callback come un array, piuttosto che individualmente. Puoi usare applyper farlo:

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`

So che funzionerà se non ho parametri come nell'esempio che hai scritto, ma quando provo a passare una funzione con parametri, genera un'eccezione e mi dice che la funzione non è definita
Amgad Fahmi,

@TiTaN: È strano, non c'è niente di speciale nel passare i parametri nel callback. Il riferimento di callback che passi nella tua funzione è un riferimento di funzione come un altro, con cui puoi fare tutte le cose normali.
TJ Crowder,

4
Chiunque ha risposto: penso che il problema di TiTaN sia che non sa come passare una funzione che richiede argomenti in un callback che non passa alcun argomento. Pensare setTimeout(). La risposta è di avvolgere il callback in una chiusura:doSomething(function(){foo('this','should','work')})
slebetman

Qualcuno punta TiTaN su una discussione (preferibilmente su SO) che discute il problema sopra, il mio motore di ricerca è debole oggi.
slebetman,

1
@Webwoman - Dipende dal tuo caso d'uso. Potresti passarlo come argomento, o includerlo in qualche tipo di oggetto settings / options o in una qualsiasi delle altre opzioni.
TJ Crowder,

78

È buona norma assicurarsi che il callback sia una funzione effettiva prima di tentare di eseguirlo:

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

  callback();
}

21
if(typeof callback == "function")avrà lo stesso risultato.
Reactgular,

22
Sì, ma se non è possibile richiamare, perché preoccuparsi di digitarlo? Questo è il punto di callback && ...
theonlygusti,

61

Il mio 2 cent. Stesso ma diverso ...

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


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

7
Adoro questo frammento, lo stavo cercando
vimal1083

10
function loadData(callback) {

    //execute other requirement

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

loadData(function(){

   //execute callback

});

6
Ti consigliamo di modificare il tuo post per aggiungere ulteriori spiegazioni su cosa fa il tuo codice e perché risolverà il problema. Una risposta che per lo più contiene solo codice (anche se funziona) di solito non aiuta l'OP a capire il loro problema. Tuttavia, in questo caso, questa è una domanda molto antica con risposte molto apprezzate già pubblicate, potrebbe non valere la pena risponderti mentre ci sono nuove domande che potrebbero fare con più attenzione.
SuperBiasedMan

1
Mi piace questa risposta alla sua dimostrazione str8 in avanti di ciò che la gente vuole vedere.
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 !");
});

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

Il risultato è:

hello world !

4

Se si desidera eseguire una funzione al termine. Una buona soluzione è ascoltare gli eventi. Ad esempio, implementerò a Dispatcher, una DispatcherEventclasse con ES6, quindi:

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

Dispatcher:

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

Buona programmazione!

p / s: Il mio codice mancante gestisce alcune eccezioni di errore


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

1

Quando si chiama la funzione di callback, potremmo usarla come di seguito:

consumingFunction(callbackFunctionName)

Esempio:

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

Questo è il Codepend con l'esempio completo.


1

Alcune delle risposte, sebbene corrette, possono essere un po 'difficili da capire. Ecco un esempio in termini di profani:

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

Il callback significa che "Jake" viene sempre aggiunto agli utenti prima di visualizzare l'elenco di utenti con console.log.

Fonte (YouTube)


0

Provare:

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

Le funzioni sono di prima classe in JavaScript ; puoi semplicemente passarli in giro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.