¿Por qué necesitamos "funciones de devolución de llamada"?


16

Estoy leyendo el libro programming in Lua. Dijo que

Los cierres proporcionan una herramienta valiosa en muchos contextos. Como hemos visto, son útiles como argumentos para funciones de orden superior como la ordenación. Los cierres son valiosos para funciones que también crean otras funciones, como nuestro ejemplo newCounter; Este mecanismo permite a los programas Lua incorporar técnicas sofisticadas de programación del mundo funcional. Los cierres también son útiles para las funciones de devolución de llamada. Un ejemplo típico aquí ocurre cuando crea botones en un kit de herramientas GUI convencional. Cada botón tiene una función de devolución de llamada que se llama cuando el usuario presiona el botón; desea que diferentes botones hagan cosas ligeramente diferentes cuando se presionan. Por ejemplo, una calculadora digital necesita diez botones similares, uno para cada dígito. Puede crear cada uno de ellos con una función como esta:

function digitButton (digit)
  return Button{label = tostring(digit),
                action = function ()
                  add_to_display(digit)
                  end}
end

Parece que si llamo al digitButton, devolverá el action(esto creará un cierre), por lo tanto, puedo acceder al digitpasado digitButton.

Mi pregunta es que:

Why we need call back functions? what situations can I apply this to?


El autor dijo:

En este ejemplo, suponemos que Button es una función del kit de herramientas que crea nuevos botones; etiqueta es la etiqueta del botón; y action es el cierre de devolución de llamada que se llamará cuando se presione el botón. La devolución de llamada se puede llamar mucho tiempo después de que digitButton realizó su tarea y después de que el dígito de la variable local se saliera del alcance, pero aún puede acceder a esta variable.

Según el autor, creo que un ejemplo similar es el siguiente:

function Button(t)
  -- maybe you should set the button here
  return t.action -- so that you can call this later
end

function add_to_display(digit)
  print ("Display the button label: " .. tostring(digit))
end

function digitButton(digit)
  return Button{label = tostring(digit),
                action = function ()
                           add_to_display(digit)
                           end}
end

click_action = digitButton(10)
click_action()

así, the callback can be called a long time after digitButton did its task and after the local variable digit went out of scope.


Respuestas:


45

Guy 1 a Guy 2: hola amigo, quiero hacer algo cuando un usuario hace clic allí, llámame cuando eso suceda, ¿de acuerdo?

Guy 2 devuelve la llamada Guy 1 cuando un usuario hace clic aquí.


1
Me gusta mucho esta broma de devolución de llamada :-P
Lucas Li

66
¿Se trata sólo de mí? No entiendo cómo esto explica por qué necesitamos funciones de devolución de llamada.
phant0m

2
Es más como Guy 1 le dice a Guy 2 "cuando sea el momento adecuado, haz esto". Guy 2 sigue con su día y cuando es el momento adecuado lo hace. Si quisiste decir que Guy 1 fue la persona que llamó a Guy 2, entonces esto es incorrecto ya que Guy 2 no devuelve la llamada a Guy 1, llama a lo que hay que hacer. De hecho, si Guy 1 es la persona que llama de Guy 2, en este escenario tendría un bucle duro en sus manos. Si quisiste que Guy 1 fuera la llamada de regreso, entonces esto es incorrecto ya que la devolución de llamada no tiene idea de quién es Guy 2 y ciertamente no le pide que llame.
rism

Esta es una explicación horrible.
dentro del

21

No, nunca devolverá la acción. El botón lo ejecutará cuando el usuario haga clic en él. Y esa es la respuesta. Necesita funciones de devolución de llamada cuando desee definir acciones que deberían ocurrir en otro componente en reacción a un evento que no controla (el bucle de eventos, que es parte del sistema, ejecutará un método del botón que a su vez se ejecutará la acción).


No estoy de acuerdo contigo He editado mi publicación y agrego algunos códigos. Sigo pensando que la acción no se ejecuta al instante.
Lucas Li

2
Tim, tienes razón, pero también Jan. "El botón lo ejecutará cuando el usuario haga clic en él", no al instante.
AlexanderBrevig

@AlexanderBrevig, sí, todavía creo que la respuesta de Jan es muy útil para mí. Me permite saber qué es la devolución de llamada y por qué necesitamos volver a llamar.
Lucas Li

La devolución de llamada es solo un nombre sucio que alguien agregó a una segunda función que funciona cuando se activa la primera y puede que se base en un criterio. Llame a su función well_done () y no tendría diferencia. La devolución de llamada es como verificar, verificar es una secuencia de mensajes, generalmente dos, y la devolución de llamada es una secuencia de funciones, generalmente dos. Si ha encadenado más de 3 devoluciones de llamada, felicidades, le gusta hacer las cosas de la manera difícil.
m3nda

16

Creo que un mejor ejemplo para el uso de devoluciones de llamada es en funciones asincrónicas. No puedo hablar por Lua, pero en JavaScript, una de las acciones asincrónicas más comunes es contactar a un servidor a través de AJAX.

La llamada AJAX es asíncrona, lo que significa que si haces algo como esto:

var ajax = ajaxCall();
if(ajax == true) {
  // Do something
}

el contenido del ifbloque no se ejecutará correctamente de manera confiable, y cuando lo hace, es solo porque la ajaxCall()función terminó antes de que la ejecución llegara a la ifdeclaración.

Sin embargo, las devoluciones de llamada eliminan este problema al garantizar que la llamada asincrónica finalice antes de llamar a la función requerida. Entonces, su código cambiaría a algo como esto:

function ajaxCallback(response) {   
  if(response == true) {
    // Do something   
  }
}

ajaxCall(ajaxCallback);

El propósito de esto es permitirle hacer cosas como recopilar datos, sin interferir con otras cosas, como dibujar la interfaz. Si la recopilación de datos fuera síncrona, la interfaz dejaría de responder mientras la aplicación esperaba obtener los datos. Una interfaz que no responde es muy mala para la experiencia del usuario, porque la mayoría de los usuarios pensarán que la aplicación se ha "bloqueado" e intentarán finalizar el proceso.

Para ver esto en acción, solo necesita mirar cualquier sitio web que realice algún tipo de actualización sin volver a cargar toda la página. Twitter, LinkedIn y los sitios de StackExchange son buenos ejemplos. El "desplazamiento sin fin" de feeds de Twitter, y las notificaciones de SE (tanto para notificaciones de usuario como para notificaciones de que una pregunta tiene nueva actividad), funcionan con llamadas asincrónicas. Cuando vea una ruleta en algún lugar, eso significa que la sección ha realizado una llamada asincrónica y está esperando que termine esa llamada, pero puede interactuar con otras cosas en la interfaz (e incluso hacer otras llamadas asincrónicas). Una vez que finalice la llamada, reaccionará y actualizará la interfaz en consecuencia.


12

Hiciste la pregunta

¿Por qué necesitamos "funciones de devolución de llamada"?

Trataré de responderlo de una manera concisa y clara (si no lo hago, consulte el enlace wiki en la parte inferior de esta respuesta).

Las devoluciones de llamada se utilizan para diferir la implementación específica de algo hasta el último momento posible.

El ejemplo del botón es una buena ilustración de esto. Supongamos que desea tener un botón en su aplicación que imprima una página en la impresora, podríamos imaginar un mundo en el que tendría que codificar una PrinterButtonclase completamente nueva para hacer esto.

Afortunadamente, tenemos la noción de devoluciones de llamada (la herencia y el uso del patrón de plantilla también es una especie de devolución de llamada semántica), por lo que no necesitamos hacer eso.

En lugar de volver a implementar cada aspecto de un botón, lo que hacemos es agregar a la implementación del botón. El Buttontiene el concepto de hacer algo cuando se presiona. Simplemente le decimos qué es ese algo.

Ese mecanismo de inyección de comportamiento en marcos se llama devoluciones de llamada.

Ejemplo:

Inyectemos un comportamiento en un botón HTML:

<button onclick="print()">Print page through a callback to the print() method</button>

En este caso, onclickapunta a una devolución de llamada, que para este ejemplo es el print()método.


http://en.wikipedia.org/wiki/Callback_(computer_programming)


Gracias, después de leer su ilustración, voy a leer el wiki cuyos ejemplos son solo devoluciones de llamada sincrónicas.
Lucas Li

diga a una función la respuesta cada vez que tenga un problema y tenga que escuchar los problemas; Enseñar una función para resolver un problema que se cuida solo.
Joe

8

¿Por qué necesitamos funciones de devolución de llamada? ¿A qué situaciones puedo aplicar esto?

Las funciones de devolución de llamada son muy útiles en la programación controlada por eventos. Le permiten configurar su programa de tal manera que los eventos activen el código correcto. Esto es muy común en programas con GUI donde los usuarios pueden hacer clic en cualquier elemento de la interfaz de usuario (como botones o elementos de menú) y se ejecutará el código apropiado.


1
+ Para eso son buenas las devoluciones de llamada, pero odio tener que escribirlas :-)
Mike Dunlavey

Cuando solo era un estudiante de primer año, mi maestro nos enseñó cómo programar en MFC, pero nunca nos dijo qué es la devolución de llamada :-(
Lucas Li

2

Qué sucede sin devoluciones de llamada:

Chico 1: Bien, estoy esperando que eso suceda. (silbidos, pulgares torcidos)

Chico 2: ¡Maldita sea! ¿Por qué Guy 1 no me muestra cosas? ¡Quiero ver que sucedan cosas! ¿Dónde están mis cosas? ¿Cómo se supone que alguien haga algo por aquí?


1

En primer lugar, el término devolución de llamada se refiere a un cierre que se está utilizando para algo.

Por ejemplo, suponga que crea una función de cierre y simplemente la almacena en una variable. No es una devolución de llamada, porque no se está utilizando para nada.

Pero, suponga que crea un cierre y lo almacena en un lugar donde se llamará cuando ocurra algo. Ahora se denomina devolución de llamada.

Por lo general, las devoluciones de llamada son creadas por diferentes partes del programa que las partes que las llaman. Por lo tanto, es fácil imaginar que algunas partes del programa están "llamando" a las otras partes.

En pocas palabras, las devoluciones de llamada permiten que una parte del programa le diga a otra parte que haga algo (cualquier cosa) cuando algo sucede.

En cuanto a las variables que se mantienen vivas debido a su uso en un cierre, esa es una característica completamente diferente llamada valores ascendentes (también conocido como "extender la vida útil de las variables locales" entre aquellos que no hablan lua). Y también es bastante útil.


Sí, Extending the lifetime of local variablestambién es decir el término upvalue.
Lucas Li

Mmm interesante. El término upvalue parece ser específico de Lua, aunque una búsqueda rápida en Google muestra que ocasionalmente se ha utilizado para describir otros idiomas. Lo agregaré a mi respuesta.
Jonathan Graef

1

Las devoluciones de llamada han existido desde los primeros días de Fortran, al menos. Por ejemplo, si tiene un solucionador ODE (ecuación diferencial ordinaria), como un solucionador Runge-Kutta, podría verse así:

subroutine rungekutta(neq, t, tend, y, deriv)
  integer neq             ! number of differential equations
  double precision t      ! initial time
  double precision tend   ! final time
  double precision y(neq) ! state vector
  ! deriv is the callback function to evaluate the state derivative
  ...
  deriv(neq, t, y, yprime) ! call deriv to get the state derivative
  ...
  deriv(neq, t, y, yprime) ! call it several times
  ...
end subroutine

Le permite al llamante personalizar el comportamiento de la subrutina al pasarle una función de propósito especial que se llamará cuando sea necesario. Esto permite que el solucionador de ODE se use para simular una amplia variedad de sistemas.


1

Tropecé con esto y quise proporcionar una respuesta más actualizada ...

Las funciones de devolución de llamada nos permiten hacer algo con los datos en un momento posterior , permitiendo que se ejecute el resto de nuestro código, en lugar de esperarlo. El código asincrónico nos permite este lujo de ejecutar cualquier cosa más adelante . El ejemplo más legible de las funciones de devolución de llamada que he encontrado son Promesas en JavaScript. En el siguiente ejemplo, cada vez que ve la función (resultado) o (newResult) o (finalResult) ... estas son funciones de devolución de llamada. El código dentro de sus llaves se ejecuta una vez que los datos vuelven del servidor. Solo en este punto tendría sentido ejecutar estas funciones ya que ahora los datos que necesitan están disponibles.

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

el código está tomado de ... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

Con suerte, esto ayuda a alguien. Esto es lo que ME ayudó a entender las devoluciones de llamada :)


1
Esto no parece ofrecer nada sustancial sobre los puntos hechos y explicados en las respuestas anteriores 9
mosquito

1
Tal vez para ti, pero esto es lo que realmente me dejó claras las devoluciones de llamadas, así que pensé en compartirlas. En mi opinión, la legibilidad del ejemplo es lo que hace que valga la pena. Estoy seguro de que podemos aceptar que la legibilidad del código es muy útil, especialmente para los principiantes.
NRV

-2

No sé lua, pero en general los métodos de devolución de llamada son algo que le gusta el subproceso múltiple Por ejemplo, en la programación de desarrollo de aplicaciones móviles, la mayoría de las aplicaciones funcionan como enviar una solicitud al servidor y jugar con la interfaz de usuario con los datos como respuesta del servidor. Cuando el usuario envía una solicitud al servidor, llevará tiempo obtener la respuesta del servidor, pero para una mejor UX, la IU no debe bloquearse.

Debido a esto, usamos múltiples hilos para hacer operaciones paralelas. Cuando recibimos la respuesta del servidor, necesitamos actualizar la interfaz de usuario. tenemos que notificar desde ese hilo para actualizar. del mundo funcional Este tipo de llamadas a funciones se denominan métodos de devolución de llamada. Cuando llama a estos métodos, el control debería haber regresado al hilo principal. Por ejemplo, los métodos de devolución de llamada son bloques en el objetivo-C.


Sí, lo que dijiste es consistente con mi comprensión después de leer el libro.
Lucas Li

2
Los métodos de devolución de llamada no son nada como subprocesos múltiples. Los métodos de devolución de llamada son simplemente punteros de función (delegados). Los hilos son sobre el cambio / utilización del contexto de la CPU. Un contraste altamente simplificado sería que el subproceso se trata de la optimización de la CPU para UX, mientras que las devoluciones de llamada se refieren al cambio de memoria para el polimorfismo. El hecho de que los subprocesos puedan ejecutar devoluciones de llamada no significa que el subproceso y las devoluciones de llamada sean similares. Los subprocesos también ejecutan métodos estáticos en clases estáticas, pero los tipos de subprocesos y estáticos no son similares.
rism
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.