¿Cómo explicar las devoluciones de llamada en inglés simple? ¿En qué se diferencian de llamar a una función de otra función tomando algún contexto de la función de llamada? ¿Cómo se puede explicar su poder a un programador novato?
¿Cómo explicar las devoluciones de llamada en inglés simple? ¿En qué se diferencian de llamar a una función de otra función tomando algún contexto de la función de llamada? ¿Cómo se puede explicar su poder a un programador novato?
Respuestas:
A menudo, una aplicación necesita ejecutar diferentes funciones en función de su contexto / estado. Para esto, usamos una variable donde almacenaríamos la información sobre la función que se llamará. Según su necesidad, la aplicación configurará esta variable con la información sobre la función que se llamará y llamará a la función utilizando la misma variable.
En javascript, el ejemplo está debajo. Aquí usamos el argumento del método como una variable donde almacenamos información sobre la función.
function processArray(arr, callback) {
var resultArr = new Array();
for (var i = arr.length-1; i >= 0; i--)
resultArr[i] = callback(arr[i]);
return resultArr;
}
var arr = [1, 2, 3, 4];
var arrReturned = processArray(arr, function(arg) {return arg * -1;});
// arrReturned would be [-1, -2, -3, -4]
function(arg)
) en la processArray(arr,callback)
función
Voy a tratar de mantener esto muy simple. Una "devolución de llamada" es cualquier función que es llamada por otra función que toma la primera función como parámetro. Muchas veces, una "devolución de llamada" es una función que se llama cuando sucede algo . Ese algo se puede llamar un "evento" en lenguaje de programador.
Imagine este escenario: está esperando un paquete en un par de días. El paquete es un regalo para tu vecino. Por lo tanto, una vez que obtenga el paquete, desea que lo traigan a los vecinos. Está fuera de la ciudad, por lo que deja instrucciones para su cónyuge.
Podrías decirles que obtengan el paquete y se lo lleven a los vecinos. Si su cónyuge fuera tan estúpido como una computadora, se sentarían en la puerta y esperarían el paquete hasta que llegara (NO HACER NADA MÁS) y luego, una vez que llegara, lo llevarían a los vecinos. Pero hay una mejor manera. Dígale a su cónyuge que UNA VEZ que reciben el paquete, deben llevarlo a los vecinos. Luego, pueden continuar con la vida normalmente HASTA que reciban el paquete.
En nuestro ejemplo, la recepción del paquete es el "evento" y la entrega a los vecinos es la "devolución de llamada". Su cónyuge "ejecuta" sus instrucciones para llevar el paquete solo cuando llegue el paquete. ¡Mucho mejor!
Este tipo de pensamiento es obvio en la vida diaria, pero las computadoras no tienen el mismo tipo de sentido común. Considere cómo los programadores normalmente escriben en un archivo:
fileObject = open(file)
# now that we have WAITED for the file to open, we can write to it
fileObject.write("We are writing to the file.")
# now we can continue doing the other, totally unrelated things our program does
Aquí, ESPERAMOS que se abra el archivo, antes de escribirlo. ¡Esto "bloquea" el flujo de ejecución, y nuestro programa no puede hacer ninguna de las otras cosas que podría necesitar hacer! ¿Qué pasaría si pudiéramos hacer esto en su lugar?
# we pass writeToFile (A CALLBACK FUNCTION!) to the open function
fileObject = open(file, writeToFile)
# execution continues flowing -- we don't wait for the file to be opened
# ONCE the file is opened we write to it, but while we wait WE CAN DO OTHER THINGS!
Resulta que hacemos esto con algunos lenguajes y marcos. ¡Es genial! Echa un vistazo a Node.js para obtener una práctica real con este tipo de pensamiento.
open
funciona. Es open
posible que se bloquee internamente mientras espera que el sistema operativo haga su magia negra, sobre la cual se ejecuta la devolución de llamada. No hay diferencia en el resultado en tal caso.
¿Cómo explicar las devoluciones de llamada en inglés simple?
En inglés simple, una función de devolución de llamada es como un Trabajador que "devuelve la llamada" a su Gerente cuando ha completado una Tarea .
¿En qué se diferencian de llamar a una función de otra función tomando algún contexto de la función de llamada?
Es cierto que está llamando a una función desde otra función, pero la clave es que la devolución de llamada se trata como un Objeto, por lo que puede cambiar qué Función llamar en función del estado del sistema (como el Patrón de Diseño de Estrategia).
¿Cómo se puede explicar su poder a un programador novato?
El poder de las devoluciones de llamada se puede ver fácilmente en los sitios web de estilo AJAX que necesitan extraer datos de un servidor. La descarga de los nuevos datos puede llevar algún tiempo. Sin devoluciones de llamada, toda su interfaz de usuario se "congelaría" mientras descarga los nuevos datos, o necesitaría actualizar toda la página en lugar de solo una parte de ella. Con una devolución de llamada, puede insertar una imagen de "cargando ahora" y reemplazarla con los nuevos datos una vez cargada.
function grabAndFreeze() {
showNowLoading(true);
var jsondata = getData('http://yourserver.com/data/messages.json');
/* User Interface 'freezes' while getting data */
processData(jsondata);
showNowLoading(false);
do_other_stuff(); // not called until data fully downloaded
}
function processData(jsondata) { // do something with the data
var count = jsondata.results ? jsondata.results.length : 0;
$('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
$('#results_messages').html(jsondata.results || '(no new messages)');
}
Aquí hay un ejemplo con una devolución de llamada, usando getJSON de jQuery :
function processDataCB(jsondata) { // callback: update UI with results
showNowLoading(false);
var count = jsondata.results ? jsondata.results.length : 0;
$('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
$('#results_messages').html(jsondata.results || '(no new messages)');
}
function grabAndGo() { // and don't freeze
showNowLoading(true);
$('#results_messages').html(now_loading_image);
$.getJSON("http://yourserver.com/data/messages.json", processDataCB);
/* Call processDataCB when data is downloaded, no frozen User Interface! */
do_other_stuff(); // called immediately
}
A menudo, la devolución de llamada necesita acceder state
desde la función de llamada usando a closure
, que es como si el Trabajador necesitara obtener información del Administrador antes de que pueda completar su Tarea . Para crear closure
, puede alinear la función para que vea los datos en el contexto de llamada:
/* Grab messages, chat users, etc by changing dtable. Run callback cb when done.*/
function grab(dtable, cb) {
if (null == dtable) { dtable = "messages"; }
var uiElem = "_" + dtable;
showNowLoading(true, dtable);
$('#results' + uiElem).html(now_loading_image);
$.getJSON("http://yourserver.com/user/"+dtable+".json", cb || function (jsondata) {
// Using a closure: can "see" dtable argument and uiElem variables above.
var count = jsondata.results ? jsondata.results.length : 0,
counterMsg = ['Fetched', count, 'new', dtable].join(' '),
// no new chatters/messages/etc
defaultResultsMsg = ['(no new ', dtable, ')'].join('');
showNowLoading(false, dtable);
$('#counter' + uiElem).text(counterMsg);
$('#results'+ uiElem).html(jsondata.results || defaultResultsMsg);
});
/* User Interface calls cb when data is downloaded */
do_other_stuff(); // called immediately
}
// update results_chatters when chatters.json data is downloaded:
grab("chatters");
// update results_messages when messages.json data is downloaded
grab("messages");
// call myCallback(jsondata) when "history.json" data is loaded:
grab("history", myCallback);
Por último, aquí hay una definición closure
de Douglas Crockford :
Las funciones se pueden definir dentro de otras funciones. La función interna tiene acceso a los vars y parámetros de la función externa. Si sobrevive una referencia a una función interna (por ejemplo, como una función de devolución de llamada), los vars de la función externa también sobreviven.
Ver también:
Me sorprende ver a tantas personas inteligentes que no hacen hincapié en la realidad que la palabra "devolución de llamada" se ha utilizado de dos maneras inconsistentes.
Ambas formas implican la personalización de una función pasando funcionalidad adicional (una definición de función, anónima o con nombre) a una función existente. es decir.
customizableFunc(customFunctionality)
Si la funcionalidad personalizada simplemente se conecta al bloque de código, usted ha personalizado la función, así.
customizableFucn(customFunctionality) {
var data = doSomthing();
customFunctionality(data);
...
}
Aunque este tipo de funcionalidad inyectada a menudo se denomina "devolución de llamada", no tiene nada de contingente. Un ejemplo muy obvio es el método forEach en el que se proporciona una función personalizada como argumento que se aplicará a cada elemento de una matriz para modificar la matriz.
Pero esto es fundamentalmente distinto del uso de funciones de "devolución de llamada" para la programación asincrónica , como en AJAX o node.js o simplemente en la asignación de funcionalidad a eventos de interacción del usuario (como los clics del mouse). En este caso, la idea es esperar a que ocurra un evento contingente antes de ejecutar la funcionalidad personalizada. Esto es obvio en el caso de la interacción del usuario, pero también es importante en los procesos de E / S (entrada / salida) que pueden llevar tiempo, como leer archivos del disco. Aquí es donde el término "devolución de llamada" tiene el sentido más obvio. Una vez que se inicia un proceso de E / S (como pedir que se lea un archivo desde el disco o un servidor para devolver datos de una solicitud http), se produce un asíncronoel programa no espera a que termine. Puede continuar con las tareas que se programan a continuación, y solo responder con la funcionalidad personalizada después de que se le haya notificado que el archivo de lectura o la solicitud http se ha completado (o que falló) y que los datos están disponibles para la funcionalidad personalizada. Es como llamar a una empresa por teléfono y dejar su número de "devolución de llamada", para que puedan llamarlo cuando alguien esté disponible para responderle. Eso es mejor que esperar en la línea por quién sabe cuánto tiempo y no poder atender otros asuntos.
El uso asincrónico implica de manera inherente algunos medios de escuchar el evento deseado (por ejemplo, la finalización del proceso de E / S) para que, cuando ocurra (y solo cuando ocurra), se ejecute la funcionalidad personalizada de "devolución de llamada". En el ejemplo obvio de AJAX, cuando los datos realmente llegan del servidor, la función de "devolución de llamada" se activa para usar esos datos para modificar el DOM y, por lo tanto, volver a dibujar la ventana del navegador en esa medida.
Recordar. Algunas personas usan la palabra "devolución de llamada" para referirse a cualquier tipo de funcionalidad personalizada que se puede inyectar en una función existente como argumento. Pero, al menos para mí, el uso más apropiado de la palabra es donde la función de "devolución de llamada" inyectada se usa de forma asíncrona, para ejecutarse solo cuando se produce un evento del que está esperando ser notificado.
Array.prototype.forEach()
y la función transmitida como un argumento a setTimeout()
, y son caballos de diferentes colores en la forma en que razona sobre su programa .
En términos no programadores, una devolución de llamada es un espacio en blanco en un programa.
Un elemento común en muchos formularios en papel es "Persona a quien llamar en caso de emergencia". Hay una línea en blanco allí. Escribe el nombre y el número de teléfono de alguien. Si ocurre una emergencia, entonces se llama a esa persona.
Esta es la clave. No cambia el formulario (el código, generalmente el de otra persona). Sin embargo, puede completar los datos faltantes ( su número).
Ejemplo 1:
Las devoluciones de llamada se utilizan como métodos personalizados, posiblemente para agregar / cambiar el comportamiento de un programa. Por ejemplo, tome un código C que realice una función, pero no sepa cómo imprimir la salida. Todo lo que puede hacer es hacer una cadena. Cuando intenta averiguar qué hacer con la cadena, ve una línea en blanco. ¡Pero el programador le dio el espacio en blanco para escribir su devolución de llamada!
En este ejemplo, no usa un lápiz para rellenar un espacio en blanco en una hoja de papel, usa la función set_print_callback(the_callback)
.
set_print_callback
es el lapizthe_callback
es su información que está completando.Ahora ha completado esta línea en blanco en el programa. Siempre que necesite imprimir la salida, verá esa línea en blanco y seguirá las instrucciones allí (es decir, llame a la función que puso allí). Prácticamente, esto permite la posibilidad de imprimir en la pantalla, en un archivo de registro, en una impresora, a través de una conexión de red, o cualquier combinación de las mismas. Has completado el espacio en blanco con lo que quieres hacer.
Ejemplo 2
Cuando le dicen que necesita llamar a un número de emergencia, va y lee lo que está escrito en el formulario en papel, y luego llama al número que leyó. Si esa línea está en blanco no se hará nada.
La programación gui funciona de la misma manera. Cuando se hace clic en un botón, el programa necesita descubrir qué hacer a continuación. Se va y busca la devolución de llamada. Esta devolución de llamada se encuentra en un espacio en blanco con la etiqueta "Esto es lo que debe hacer cuando se hace clic en Button1"
La mayoría de los IDEs completarán automáticamente el espacio en blanco por usted (escriba el método básico) cuando se lo pida (por ejemplo button1_clicked
). Sin embargo, ese espacio en blanco puede tener cualquier método que quieras, por favor . Puede llamar al método run_computations
o butter_the_biscuits
siempre que ponga el nombre de la devolución de llamada en el espacio en blanco adecuado. Puede poner "555-555-1212" en el número de emergencia en blanco. No tiene mucho sentido, pero está permitido.
Nota final: ¿Esa línea en blanco que está completando con la devolución de llamada? Se puede borrar y reescribir a voluntad. (si debería o no es otra pregunta, pero eso es parte de su poder)
Siempre es mejor comenzar con un ejemplo :).
Supongamos que tiene dos módulos A y B.
Desea que se le notifique al módulo A cuando se produce algún evento / condición en el módulo B. Sin embargo, el módulo B no tiene idea de su módulo A. Todo lo que sabe es una dirección a una función particular (del módulo A) a través de un puntero de función que es proporcionado por el módulo A.
Entonces, todo lo que B tiene que hacer ahora, es "devolución de llamada" en el módulo A cuando ocurre un evento / condición particular usando el puntero de función. A puede hacer más procesamiento dentro de la función de devolución de llamada.
*) Una clara ventaja aquí es que está abstrayendo todo lo relacionado con el módulo A del módulo B. El módulo B no tiene que preocuparse por quién / qué es el módulo A.
Imagina que necesitas una función que devuelva 10 al cuadrado para escribir una función:
function tenSquared() {return 10*10;}
Luego necesitas 9 al cuadrado para escribir otra función:
function nineSquared() {return 9*9;}
Eventualmente, reemplazará todo esto con una función genérica:
function square(x) {return x*x;}
Se aplica exactamente el mismo pensamiento para las devoluciones de llamada. Tiene una función que hace algo y, cuando termina, llama a doA:
function computeA(){
...
doA(result);
}
Más tarde, desea exactamente la misma función para llamar a doB; en su lugar, podría duplicar toda la función:
function computeB(){
...
doB(result);
}
O podría pasar una función de devolución de llamada como variable y solo tener que tener la función una vez:
function compute(callback){
...
callback(result);
}
Entonces solo tiene que llamar a compute (doA) y compute (doB).
Más allá de simplificar el código, permite que el código asíncrono le permita saber que se ha completado llamando a su función arbitraria al finalizar, similar a cuando llama a alguien por teléfono y deja un número de devolución de llamada.
Johny, el programador, necesita una engrapadora, por lo que va al departamento de suministros de oficina y solicita una, luego de completar el formulario de solicitud, puede quedarse allí y esperar a que el empleado busque en la bodega la grapadora (como una llamada de función de bloqueo ) o ir a hacer otra cosa mientras tanto.
Como esto suele llevar tiempo, johny pone una nota junto con el formulario de solicitud pidiéndoles que lo llamen cuando la grapadora esté lista para que la recojan, por lo tanto, puede ir a hacer otra cosa como tomar una siesta en su escritorio.
Te sientes mal, así que vas al médico. Él te examina y determina que necesitas algún medicamento. Prescribe algunos medicamentos y llama la receta a su farmacia local. Ve a casa. Más tarde, su farmacia llama para decirle que su receta está lista. Ve y recógelo.
Hay dos puntos para explicar, uno es cómo funciona una devolución de llamada (pasar una función que se puede llamar sin ningún conocimiento de su contexto), y el otro para qué se utiliza (manejar eventos de forma asincrónica).
La analogía de esperar a que llegue un paquete que ha sido utilizado por otras respuestas es buena para explicar ambas. En un programa de computadora, le dirías a la computadora que espere un paquete. Por lo general, ahora se sentaría allí y esperaría (y no haría nada más) hasta que llegue el paquete, posiblemente indefinidamente si nunca llega. Para los humanos, esto suena tonto, pero sin más medidas, esto es totalmente natural para una computadora.
Ahora la devolución de llamada sería el timbre de tu puerta principal. Proporcionas al servicio de paquetería una forma de notificarte sobre la llegada de la parcela sin que tengan que saber dónde (incluso si) estás en la casa o cómo funciona el timbre. (Por ejemplo, algunas "campanas" en realidad envían una llamada telefónica). Debido a que proporcionó una "función de devolución de llamada" que se puede "llamar" en cualquier momento, fuera de contexto, ahora puede dejar de sentarse en el porche y "manejar el evento "(de la llegada del paquete) cuando sea el momento.
Imagina que un amigo está saliendo de tu casa y le dices "Llámame cuando llegues a casa para que sepa que llegaste a salvo"; eso es (literalmente) una devolución de llamada . Eso es lo que es una función de devolución de llamada, independientemente del idioma. Desea que algún procedimiento le devuelva el control cuando haya completado alguna tarea, por lo que le da una función para que le devuelva la llamada.
En Python, por ejemplo,
grabDBValue( (lambda x: passValueToGUIWindow(x) ))
grabDBValue
podría escribirse solo para obtener un valor de una base de datos y luego permitirle especificar qué hacer realmente con el valor, para que acepte una función. No sabe cuándo o si grabDBValue
volverá, pero si / cuándo lo hace, sabe lo que quiere que haga. Aquí, paso una función anónima (o lambda ) que envía el valor a una ventana GUI. Podría cambiar fácilmente el comportamiento del programa haciendo esto:
grabDBValue( (lambda x: passToLogger(x) ))
Las devoluciones de llamada funcionan bien en lenguajes donde las funciones son valores de primera clase , al igual que los enteros habituales, cadenas de caracteres, booleanos, etc. En C, puede "pasar" una función pasando un puntero y la persona que llama puede usar eso; en Java, la persona que llama solicitará una clase estática de cierto tipo con un nombre de método determinado ya que no hay funciones ("métodos" realmente) fuera de las clases; y en la mayoría de los otros lenguajes dinámicos puedes pasar una función con una sintaxis simple.
En idiomas con alcance léxico (como Scheme o Perl), puede hacer un truco como este:
my $var = 2;
my $val = someCallerBackFunction(sub callback { return $var * 3; });
# Perlistas note: I know the sub doesn't need a name, this is for illustration
$val
en este caso será 6
porque la devolución de llamada tiene acceso a las variables declaradas en el entorno léxico donde se definió. El alcance léxico y las devoluciones de llamadas anónimas son una combinación poderosa que garantiza un mayor estudio para el programador novato.
Tienes un código que quieres ejecutar. Normalmente, cuando lo llama, está esperando que termine antes de continuar (lo que puede hacer que su aplicación se vuelva gris / produzca un tiempo de giro para un cursor).
Un método alternativo es ejecutar este código en paralelo y continuar con su propio trabajo. Pero, ¿qué pasa si su código original necesita hacer cosas diferentes dependiendo de la respuesta del código que llamó? Bueno, en ese caso, puede pasar el nombre / ubicación del código al que desea que llame cuando esté listo. Esta es una "devolución de llamada".
Código normal: Solicitar información-> Información de proceso-> Manejar los resultados del procesamiento-> Continuar haciendo otras cosas.
Con devoluciones de llamada: Solicite información-> Información de proceso-> Continúe haciendo otras cosas. Y en algún momento posterior-> Tratar con los resultados del procesamiento.
Sin devolución de llamada ni otros recursos de programación especiales (como subprocesos y otros), un programa es exactamente una secuencia de instrucciones que se ejecutan secuencialmente una tras otra , e incluso con un tipo de "comportamiento dinámico" determinado por ciertas condiciones, todos los escenarios posibles deberá ser previamente programado .
Entonces, si necesitamos proporcionar un comportamiento dinámico real a un programa, podemos usar la devolución de llamada. Con la devolución de llamada puede indicar por parámetros, un programa para llamar a otro programa que proporciona algunos parámetros previamente definidos y puede esperar algunos resultados ( esta es la firma del contrato o la operación ), por lo que estos resultados pueden ser producidos / procesados por un programa de terceros que no No se sabe previamente.
Esta técnica es la base del polimorfismo aplicado a programas, funciones, objetos y todas las demás unidades de código ejecutadas por computadoras.
El mundo humano utilizado como ejemplo para la devolución de llamada se explica bien cuando está haciendo un trabajo, supongamos que es un pintor ( aquí está el programa principal, que pinta ) y llame a su cliente a veces para pedirle que apruebe el resultado de su trabajo , entonces, decide si la imagen es buena ( su cliente es el programa de terceros ).
En el ejemplo anterior, usted es pintor y "delega" a otros el trabajo para aprobar el resultado, la imagen es el parámetro y cada nuevo cliente (la "función" devuelta) cambia el resultado de su trabajo decidiendo lo que quiere sobre la imagen ( la decisión tomada por los clientes es el resultado devuelto de la "función de devolución de llamada" ).
Espero que esta explicación pueda ser útil.
Supongamos que me ibas a dar una tarea potencialmente larga: obtener los nombres de las primeras cinco personas únicas con las que te encuentres. Esto podría llevar días si estoy en un área escasamente poblada. No estás realmente interesado en sentarte en tus manos mientras corro, así que dices: "Cuando tengas la lista, llámame a mi celular y léemela. Aquí está el número".
Me ha dado una referencia de devolución de llamada, una función que se supone que debo ejecutar para poder seguir procesando.
En JavaScript podría verse así:
var lottoNumbers = [];
var callback = function(theNames) {
for (var i=0; i<theNames.length; i++) {
lottoNumbers.push(theNames[i].length);
}
};
db.executeQuery("SELECT name " +
"FROM tblEveryOneInTheWholeWorld " +
"ORDER BY proximity DESC " +
"LIMIT 5", callback);
while (lottoNumbers.length < 5) {
playGolf();
}
playLotto(lottoNumbers);
Esto probablemente podría mejorarse de muchas maneras. Por ejemplo, podría proporcionar una segunda devolución de llamada: si termina tardando más de una hora, llame al teléfono rojo y dígale a la persona que responde que ha agotado el tiempo de espera.
Las devoluciones de llamada se describen más fácilmente en términos del sistema telefónico. Una llamada de función es análoga a llamar a alguien por teléfono, hacerle una pregunta, obtener una respuesta y colgar; agregar una devolución de llamada cambia la analogía para que después de hacerle una pregunta, también le dé su nombre y número para que pueda devolverle la llamada con la respuesta. - Paul Jakubik, "Implementaciones de devolución de llamada en C ++"
Una devolución de llamada es una función que será llamada por una segunda función. Esta segunda función no sabe de antemano a qué función llamará. Entonces, la identidad de la función de devolución de llamada se almacena en algún lugar, o se pasa a la segunda función como parámetro. Esta "identidad", dependiendo del lenguaje de programación, podría ser la dirección de la devolución de llamada, o algún otro tipo de puntero, o podría ser el nombre de la función. El principal es el mismo, almacenamos o pasamos información que identifica inequívocamente la función.
Cuando llegue el momento, la segunda función puede llamar a la devolución de llamada, proporcionando parámetros dependiendo de las circunstancias en ese momento. Incluso podría elegir la devolución de llamada de un conjunto de posibles devoluciones de llamada. El lenguaje de programación debe proporcionar algún tipo de sintaxis para permitir que la segunda función llame a la devolución de llamada, conociendo su "identidad".
Este mecanismo tiene muchos usos posibles. Con las devoluciones de llamada, el diseñador de una función puede permitir que se personalice haciendo que llame a cualquier devolución de llamada que se proporcione. Por ejemplo, una función de clasificación podría tomar una devolución de llamada como parámetro, y esta devolución de llamada podría ser una función para comparar dos elementos para decidir cuál es el primero.
Por cierto, dependiendo del lenguaje de programación, la palabra "función" en la discusión anterior podría ser reemplazada por "bloque", "cierre", "lambda", etc.
Por lo general, enviamos variables a las funciones. Supongamos que tiene una tarea donde la variable necesita ser procesada antes de ser dada como argumento: puede usar la devolución de llamada.
function1(var1, var2)
Es la forma habitual.
¿Qué sucede si quiero var2
ser procesado y luego enviado como argumento?
function1(var1, function2(var2))
Este es un tipo de devolución de llamada, donde function2
ejecuta un código y devuelve una variable a la función inicial.
Una explicación metafórica:
Tengo un paquete que quiero entregar a un amigo, y también quiero saber cuándo lo recibe mi amigo.
Entonces llevo el paquete a la oficina de correos y les pido que lo entreguen. Si quiero saber cuándo mi amigo recibe el paquete, tengo dos opciones:
(a) Puedo esperar en la oficina de correos hasta que se entregue.
(b) Recibiré un correo electrónico cuando se entregue.
La opción (b) es análoga a una devolución de llamada.
Para enseñar devoluciones de llamada, primero debe enseñar el puntero. Una vez que los estudiantes comprendan la idea de puntero a una variable, la idea de devoluciones de llamada será más fácil. Suponiendo que está utilizando C / C ++, se pueden seguir estos pasos.
Puede haber muchas más cosas. Involucre a los estudiantes y ellos descubrirán. Espero que esto ayude.
En inglés simple, una devolución de llamada es una promesa. Joe, Jane, David y Samantha comparten un viaje compartido al trabajo. Joe conduce hoy. Jane, David y Samantha tienen un par de opciones:
Opción 1: Esto es más como un ejemplo de sondeo en el que Jane estaría atrapada en un "bucle" para verificar si Joe está afuera. Jane no puede hacer nada más mientras tanto.
Opción 2: este es el ejemplo de devolución de llamada. Jane le dice a Joe que toque el timbre cuando esté afuera. Ella le da una "función" para tocar el timbre de la puerta. Joe no necesita saber cómo funciona el timbre de la puerta o dónde está, solo necesita llamar a esa función, es decir, tocar el timbre de la puerta cuando esté allí.
Las devoluciones de llamada son impulsadas por "eventos". En este ejemplo, el "evento" es la llegada de Joe. En Ajax, por ejemplo, los eventos pueden ser "exitosos" o "fallidos" de la solicitud asincrónica y cada uno puede tener la misma o diferente devolución de llamada.
En términos de aplicaciones JavaScript y devoluciones de llamada. También necesitamos entender los "cierres" y el contexto de la aplicación. A qué se refiere "esto" puede confundir fácilmente a los desarrolladores de JavaScript. En este ejemplo, dentro del método / devolución de llamada "ring_the_door_bell ()" de cada persona, puede haber algunos otros métodos que cada persona debe hacer según su rutina matutina, por ejemplo. "apagar la televisión()". Queremos que "esto" se refiera al objeto "Jane" o al objeto "David" para que cada uno pueda configurar cualquier otra cosa que necesite antes de que Joe los recoja. Aquí es donde configurar la devolución de llamada con Joe requiere parodiar el método para que "esto" se refiera al objeto correcto.
¡Espero que ayude!
Creo que es una tarea bastante fácil de explicar.
Al principio, la devolución de llamada son funciones normales.
Y además, llamamos a esta función (llamémosla A) desde dentro de otra función (llamémosla B).
La magia sobre esto es que yo decido, qué función debe llamar la función desde fuera de B.
En el momento en que escribo la función BI no sé a qué función de devolución de llamada se debe llamar. En el momento en que llamo a la función BI, también le digo a esta función que llame a la función A. Eso es todo.
¿Qué es una función de devolución de llamada?
La respuesta simple a esta primera pregunta es que una función de devolución de llamada es una función que se llama a través de un puntero de función. Si pasa el puntero (dirección) de una función como argumento a otra, cuando ese puntero se usa para llamar a la función a la que apunta, se dice que se realiza una devolución de llamada.
La función de devolución de llamada es difícil de rastrear, pero a veces es muy útil. Especialmente cuando estás diseñando bibliotecas. La función de devolución de llamada es como pedirle a su usuario que le dé un nombre de función, y usted llamará a esa función bajo ciertas condiciones.
Por ejemplo, escribe un temporizador de devolución de llamada. Le permite especificar la duración y qué función llamar, y la función será la devolución de llamada en consecuencia. "Ejecute myfunction () cada 10 segundos durante 5 veces"
O puede crear un directorio de funciones, pasando una lista del nombre de la función y solicitar a la biblioteca que devuelva la llamada en consecuencia. "Devolución de llamada exitosa () si es exitosa, devolución de llamada fallida () si falla".
Veamos un ejemplo simple de puntero de función
void cbfunc()
{
printf("called");
}
int main ()
{
/* function pointer */
void (*callback)(void);
/* point to your callback function */
callback=(void *)cbfunc;
/* perform callback */
callback();
return 0;
}
¿Cómo pasar argumentos a la función de devolución de llamada?
Se observó que el puntero de función para implementar la devolución de llamada toma nulo *, lo que indica que puede tomar cualquier tipo de variable, incluida la estructura. Por lo tanto, puede pasar varios argumentos por estructura.
typedef struct myst
{
int a;
char b[10];
}myst;
void cbfunc(myst *mt)
{
fprintf(stdout,"called %d %s.",mt->a,mt->b);
}
int main()
{
/* func pointer */
void (*callback)(void *); //param
myst m;
m.a=10;
strcpy(m.b,"123");
callback = (void*)cbfunc; /* point to callback function */
callback(&m); /* perform callback and pass in the param */
return 0;
}
Una devolución de llamada es un método programado para ejecutarse cuando se cumple una condición.
Un ejemplo del "mundo real" es una tienda local de videojuegos. Estás esperando Half-Life 3. En lugar de ir a la tienda todos los días para ver si está dentro, registras tu correo electrónico en una lista para recibir una notificación cuando el juego esté disponible. El correo electrónico se convierte en su "devolución de llamada" y la condición a cumplir es la disponibilidad del juego.
Un ejemplo de "programadores" es una página web en la que desea realizar una acción cuando se hace clic en un botón. Registra un método de devolución de llamada para un botón y continúa realizando otras tareas. Cuando / si el usuario presiona el botón, el navegador mirará la lista de devoluciones de llamada para ese evento y llamará a su método.
Una devolución de llamada es una forma de manejar eventos de forma asincrónica. Nunca puede saber cuándo se ejecutará la devolución de llamada o si se ejecutará en absoluto. La ventaja es que libera su programa y ciclos de CPU para realizar otras tareas mientras espera la respuesta.
Claro y simple: una devolución de llamada es una función que se le da a otra función, para que pueda llamarla .
Por lo general, se llama cuando se completa alguna operación. Como crea la devolución de llamada antes de dársela a la otra función, puede inicializarla con información de contexto desde el sitio de la llamada. Es por eso que se llama una llamada * back *: la primera función vuelve a llamar al contexto desde donde se llamó.
“En la programación de computadoras, una devolución de llamada es una referencia al código ejecutable, o un fragmento de código ejecutable, que se pasa como argumento a otro código. Esto permite que una capa de software de nivel inferior llame a una subrutina (o función) definida en una capa de nivel superior ". - Wikipedia
Devolución de llamada en C usando el puntero de función
En C, la devolución de llamada se implementa utilizando el puntero de función. Puntero de función: como su nombre indica, es un puntero a una función.
Por ejemplo, int (* ptrFunc) ();
Aquí, ptrFunc es un puntero a una función que no toma argumentos y devuelve un entero. NO olvide poner el paréntesis, de lo contrario, el compilador supondrá que ptrFunc es un nombre de función normal, que no toma nada y devuelve un puntero a un entero.
Aquí hay un código para demostrar el puntero de la función.
#include<stdio.h>
int func(int, int);
int main(void)
{
int result1,result2;
/* declaring a pointer to a function which takes
two int arguments and returns an integer as result */
int (*ptrFunc)(int,int);
/* assigning ptrFunc to func's address */
ptrFunc=func;
/* calling func() through explicit dereference */
result1 = (*ptrFunc)(10,20);
/* calling func() through implicit dereference */
result2 = ptrFunc(10,20);
printf("result1 = %d result2 = %d\n",result1,result2);
return 0;
}
int func(int x, int y)
{
return x+y;
}
Ahora intentemos comprender el concepto de Callback en C usando el puntero de función.
El programa completo tiene tres archivos: callback.c, reg_callback.h y reg_callback.c.
/* callback.c */
#include<stdio.h>
#include"reg_callback.h"
/* callback function definition goes here */
void my_callback(void)
{
printf("inside my_callback\n");
}
int main(void)
{
/* initialize function pointer to
my_callback */
callback ptr_my_callback=my_callback;
printf("This is a program demonstrating function callback\n");
/* register our callback function */
register_callback(ptr_my_callback);
printf("back inside main program\n");
return 0;
}
/* reg_callback.h */
typedef void (*callback)(void);
void register_callback(callback ptr_reg_callback);
/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"
/* registration goes here */
void register_callback(callback ptr_reg_callback)
{
printf("inside register_callback\n");
/* calling our callback function my_callback */
(*ptr_reg_callback)();
}
Si ejecutamos este programa, la salida será
Este es un programa que muestra la función de devolución de llamada dentro de register_callback dentro de my_callback dentro del programa principal
La función de capa superior llama a una función de capa inferior como una llamada normal y el mecanismo de devolución de llamada permite que la función de capa inferior llame a la función de capa superior a través de un puntero a una función de devolución de llamada.
Devolución de llamada en Java usando la interfaz
Java no tiene el concepto de puntero de función Implementa el mecanismo de devolución de llamada a través de su mecanismo de interfaz Aquí, en lugar de un puntero de función, declaramos que una interfaz tiene un método que se llamará cuando el destinatario finalice su tarea
Permítanme demostrarlo a través de un ejemplo:
La interfaz de devolución de llamada
public interface Callback
{
public void notify(Result result);
}
La persona que llama o la clase de nivel superior
public Class Caller implements Callback
{
Callee ce = new Callee(this); //pass self to the callee
//Other functionality
//Call the Asynctask
ce.doAsynctask();
public void notify(Result result){
//Got the result after the callee has finished the task
//Can do whatever i want with the result
}
}
La función Callee o la capa inferior
public Class Callee {
Callback cb;
Callee(Callback cb){
this.cb = cb;
}
doAsynctask(){
//do the long running task
//get the result
cb.notify(result);//after the task is completed, notify the caller
}
}
Devolución de llamada utilizando el patrón EventListener
Este patrón se utiliza para notificar de 0 a n números de observadores / oyentes que una tarea en particular ha finalizado
La diferencia entre el mecanismo de devolución de llamada y el mecanismo EventListener / Observer es que en la devolución de llamada, la persona que llama notifica a la persona que llama, mientras que en Eventlisener / Observer, la persona que llama puede notificar a cualquier persona interesada en ese evento (la notificación puede ir a otras partes del aplicación que no ha activado la tarea)
Déjame explicarte a través de un ejemplo.
La interfaz de eventos
public interface Events {
public void clickEvent();
public void longClickEvent();
}
Widget de clase
package com.som_itsolutions.training.java.exampleeventlistener;
import java.util.ArrayList;
import java.util.Iterator;
public class Widget implements Events{
ArrayList<OnClickEventListener> mClickEventListener = new ArrayList<OnClickEventListener>();
ArrayList<OnLongClickEventListener> mLongClickEventListener = new ArrayList<OnLongClickEventListener>();
@Override
public void clickEvent() {
// TODO Auto-generated method stub
Iterator<OnClickEventListener> it = mClickEventListener.iterator();
while(it.hasNext()){
OnClickEventListener li = it.next();
li.onClick(this);
}
}
@Override
public void longClickEvent() {
// TODO Auto-generated method stub
Iterator<OnLongClickEventListener> it = mLongClickEventListener.iterator();
while(it.hasNext()){
OnLongClickEventListener li = it.next();
li.onLongClick(this);
}
}
public interface OnClickEventListener
{
public void onClick (Widget source);
}
public interface OnLongClickEventListener
{
public void onLongClick (Widget source);
}
public void setOnClickEventListner(OnClickEventListener li){
mClickEventListener.add(li);
}
public void setOnLongClickEventListner(OnLongClickEventListener li){
mLongClickEventListener.add(li);
}
}
Botón de clase
public class Button extends Widget{
private String mButtonText;
public Button (){
}
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}
Casilla de verificación de clase
public class CheckBox extends Widget{
private boolean checked;
public CheckBox() {
checked = false;
}
public boolean isChecked(){
return (checked == true);
}
public void setCheck(boolean checked){
this.checked = checked;
}
}
Clase de actividad
paquete com.som_itsolutions.training.java.exampleeventlistener;
public class Activity implements Widget.OnClickEventListener
{
public Button mButton;
public CheckBox mCheckBox;
private static Activity mActivityHandler;
public static Activity getActivityHandle(){
return mActivityHandler;
}
public Activity ()
{
mActivityHandler = this;
mButton = new Button();
mButton.setOnClickEventListner(this);
mCheckBox = new CheckBox();
mCheckBox.setOnClickEventListner(this);
}
public void onClick (Widget source)
{
if(source == mButton){
mButton.setButtonText("Thank you for clicking me...");
System.out.println(((Button) mButton).getButtonText());
}
if(source == mCheckBox){
if(mCheckBox.isChecked()==false){
mCheckBox.setCheck(true);
System.out.println("The checkbox is checked...");
}
else{
mCheckBox.setCheck(false);
System.out.println("The checkbox is not checked...");
}
}
}
public void doSomeWork(Widget source){
source.clickEvent();
}
}
Otra clase
public class OtherClass implements Widget.OnClickEventListener{
Button mButton;
public OtherClass(){
mButton = Activity.getActivityHandle().mButton;
mButton.setOnClickEventListner(this);//interested in the click event //of the button
}
@Override
public void onClick(Widget source) {
if(source == mButton){
System.out.println("Other Class has also received the event notification...");
}
}
Clase principal
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Activity a = new Activity();
OtherClass o = new OtherClass();
a.doSomeWork(a.mButton);
a.doSomeWork(a.mCheckBox);
}
}
Como puede ver en el código anterior, tenemos una interfaz llamada eventos que básicamente enumera todos los eventos que pueden ocurrir para nuestra aplicación. La clase Widget es la clase base para todos los componentes de la interfaz de usuario, como Button, Checkbox. Estos componentes de la interfaz de usuario son los objetos que realmente reciben los eventos del código marco. La clase de widget implementa la interfaz de eventos y también tiene dos interfaces anidadas, a saber, OnClickEventListener y OnLongClickEventListener
Estas dos interfaces son responsables de escuchar los eventos que pueden ocurrir en los componentes de la interfaz de usuario derivados del widget como Button o Checkbox. Entonces, si comparamos este ejemplo con el ejemplo anterior de devolución de llamada utilizando la interfaz Java, estas dos interfaces funcionan como la interfaz de devolución de llamada. Entonces el código de nivel superior (Here Activity) implementa estas dos interfaces. Y cada vez que se produzca un evento en un widget, se llamará al código de nivel superior (o al método de estas interfaces implementado en el código de nivel superior, que es aquí Actividad).
Ahora déjenme discutir la diferencia básica entre Callback y el patrón Eventlistener. Como hemos mencionado que usando la devolución de llamada, la persona que llama puede notificar solo a una persona que llama. Pero en el caso del patrón EventListener, cualquier otra parte o clase de la Aplicación puede registrarse para los eventos que pueden ocurrir en el Botón o Casilla de verificación. El ejemplo de este tipo de clase es el OtherClass. Si ve el código de la Otra Clase, encontrará que se ha registrado como un oyente del ClickEvent que puede ocurrir en el Botón definido en la Actividad. La parte interesante es que, además de la Actividad (la persona que llama), esta Otra Clase también se notificará cada vez que ocurra el evento de clic en el Botón.
Callbacks le permite insertar su propio código en otro bloque de código que se ejecutará en otro momento, que modifica o agrega al comportamiento de ese otro bloque de código para satisfacer sus necesidades. Obtiene flexibilidad y personalización al tiempo que puede tener un código más fácil de mantener.
Menos código duro = más fácil de mantener y cambiar = menos tiempo = más valor comercial = genialidad.
Por ejemplo, en javascript, utilizando Underscore.js, puede encontrar todos los elementos pares en una matriz como esta:
var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]
Ejemplo cortesía de Underscore.js: http://documentcloud.github.com/underscore/#filter
[editado] cuando tenemos dos funciones, digamos functionA y functionB , si functionA depende de functionB .
luego llamamos a functionB como una función de devolución de llamada . Esto se usa ampliamente en Spring Framework.
Piense en un método como darle una tarea a un compañero de trabajo. Una tarea simple podría ser la siguiente:
Solve these equations:
x + 2 = y
2 * x = 3 * y
Su compañero de trabajo diligentemente hace los cálculos y le da el siguiente resultado:
x = -6
y = -4
Pero su compañero de trabajo tiene un problema, no siempre comprende las anotaciones, como ^
, pero las comprende por su descripción. Tales como exponent
. Cada vez que encuentra uno de estos, obtienes lo siguiente:
I don't understand "^"
Esto requiere que vuelva a escribir todo su conjunto de instrucciones nuevamente después de explicar lo que el personaje significa para su compañero de trabajo, y él no siempre recuerda entre preguntas. Y también tiene dificultades para recordar tus consejos, como preguntarme. Sin embargo, siempre sigue tus instrucciones escritas lo mejor que puede.
Piensa en una solución, solo agrega lo siguiente a todas sus instrucciones:
If you have any questions about symbols, call me at extension 1234 and I will tell you its name.
Ahora, cuando tiene un problema, lo llama y le pregunta, en lugar de darle una mala respuesta y hacer que el proceso se reinicie.
Esto en términos de descarga de una página web:
Su programa se ejecuta en un teléfono celular y solicita la página web http://www.google.com . Si escribe su programa sincrónicamente, la función que escribe para descargar los datos se ejecutará continuamente hasta que se descarguen todos los datos. Esto significa que su IU no se actualizará y básicamente aparecerá congelada. Si escribe su programa utilizando devoluciones de llamada, solicita los datos y dice "ejecutar esta función cuando haya terminado". Esto permite que la interfaz de usuario todavía permita la interacción del usuario mientras se descarga el archivo. Una vez que la página web ha finalizado la descarga, se llama a su función de resultado (devolución de llamada) y puede manejar los datos.
Básicamente, le permite solicitar algo y continuar ejecutando mientras espera el resultado. Una vez que el resultado vuelve a usted a través de una función de devolución de llamada, puede retomar la operación donde la dejó.