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?


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`

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

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>

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

});

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 !

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


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

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.


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)


0

Tratar:

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

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.