Crear una devolución de llamada personalizada en JavaScript

322

Todo lo que necesito hacer es ejecutar una función de devolución de llamada cuando finalice la ejecución de mi función actual.

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

Un consumidor para esta función debería ser así:

object.LoadData(success);

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

¿Cómo implemento esto?

Amgad Fahmi
fuente
3
object.LoadData(success)la llamada debe ser posterior a la function success definición. De lo contrario, recibirá un error que le indicará que la función no está definida.
J. Bruni

Respuestas:

574

En realidad, su código funcionará tal cual, simplemente declare su devolución de llamada como argumento y puede llamarlo directamente usando el nombre del argumento.

Los basicos

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

Eso llamará doSomething, lo que llamará foo, lo que alertará "las cosas van aquí".

Tenga en cuenta que es muy importante pasar la referencia de función ( foo), en lugar de llamar a la función y pasar su resultado ( foo()). En su pregunta, lo hace correctamente, pero vale la pena señalarlo porque es un error común.

Cosas más avanzadas

A veces desea llamar a la devolución de llamada para que vea un valor específico para this. Puede hacerlo fácilmente con la callfunción de 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`

También puedes pasar argumentos:

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 veces es útil pasar los argumentos que desea dar la devolución de llamada como una matriz, en lugar de individualmente. Puedes usar applypara hacer eso:

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`
TJ Crowder
fuente
Sé que funcionará si no tengo ningún parámetro como el ejemplo que escribió, pero cuando intento pasar una función con parámetros, arroja una excepción y me dice que la función no está definida
Amgad Fahmi,
@TiTaN: Eso es extraño, no hay nada especial en pasar parámetros a la devolución de llamada. La referencia de devolución de llamada que pasa a su función es una referencia de función como cualquier otra, puede hacer todas las cosas normales con ella.
TJ Crowder
44
@todos los que respondieron: Creo que el problema de TiTaN es que no sabe cómo pasar una función que requiere argumentos en una devolución de llamada que no pasa ningún argumento. Piense setTimeout(). La respuesta es envolver la devolución de llamada en un cierre:doSomething(function(){foo('this','should','work')})
slebetman
Alguien señala TiTaN a un hilo (preferiblemente en SO) discutiendo el tema anterior, mi búsqueda fu es débil hoy.
slebetman
1
@Webwoman: depende de su caso de uso. Puede pasarlo como argumento, o incluirlo en algún tipo de objeto de configuración / opciones, o cualquiera de varias otras opciones.
TJ Crowder
78

Es una buena práctica asegurarse de que la devolución de llamada sea una función real antes de intentar ejecutarla:

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

  callback();
}
Donald A Nummer Jr
fuente
21
if(typeof callback == "function")Tendrá el mismo resultado.
Reactgular
22
Sí, pero si no hay devolución de llamada, ¿por qué molestarse en escribirlo? Ese es el punto de callback && ...
theonlygusti
61

Mi 2 centavo Lo mismo pero diferente...

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


    function dosomething(damsg, callback){
        alert(damsg);
        if(typeof callback == "function") 
        callback();
    }
</script>
K. Kilian Lindberg
fuente
77
Me encanta este fragmento, estaba buscando esto
vimal1083
10
function loadData(callback) {

    //execute other requirement

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

loadData(function(){

   //execute callback

});
Arun Bahal
fuente
66
Considere editar su publicación para agregar más explicaciones sobre lo que hace su código y por qué resolverá el problema. Una respuesta que en su mayoría solo contiene código (incluso si está funcionando) generalmente no ayudará al OP a comprender su problema. Sin embargo, en este caso, esta es una pregunta muy antigua con respuestas altamente consideradas ya publicadas, puede que no valga la pena responderlas cuando hay preguntas más nuevas que podrían hacer con más atención.
SuperBiasedMan
1
Me gusta esta respuesta, es una demostración de lo que la gente quiere ver.
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 !");
});

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

El resultado es:

hello world !
Eyad Farra
fuente
4

Si desea ejecutar una función cuando se hace algo. Una de las buenas soluciones es escuchar eventos. Por ejemplo, implementaré a Dispatcher, una DispatcherEventclase con ES6, luego:

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

Despachador:

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

¡Feliz codificación!

p / s: falta mi código para manejar algunas excepciones de error

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

Al llamar a la función de devolución de llamada, podríamos usarla como a continuación:

consumingFunction(callbackFunctionName)

Ejemplo:

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

Este es el Codepend con el ejemplo completo.

Eric Tan
fuente
1

Algunas de las respuestas, aunque correctas, pueden ser un poco difíciles de entender. Aquí hay un ejemplo en términos 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);

La devolución de llamada significa que "Jake" siempre se agrega a los usuarios antes de mostrar la lista de usuarios con console.log.

Fuente (YouTube)

Dan Bray
fuente
0

Tratar:

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

Las funciones son de primera clase en JavaScript ; puedes pasarlos por ahí.

K Prime
fuente