¿Qué es una función de devolución de llamada?

Resuelto asked hace 15 años • 0 respuestas

¿Qué es una función de devolución de llamada?

 avatar May 05 '09 17:05
Aceptado

Los desarrolladores a menudo se confunden sobre lo que es una devolución de llamada debido al nombre de la maldita cosa.

Una función de devolución de llamada es una función que es:

  • accesible mediante otra función, y
  • se invoca después de la primera función si esa primera función se completa

Una buena forma de imaginar cómo funciona una función de devolución de llamada es que es una función que se " llama al final " de la función a la que se pasa.

Quizás un mejor nombre sería una función de "llamada después" .

Esta construcción es muy útil para comportamientos asincrónicos en los que queremos que se lleve a cabo una actividad cada vez que se completa un evento anterior.

Pseudocódigo:

// A function which accepts another function as an argument
// (and will automatically invoke that function when it completes - note that there is no explicit call to callbackFunction)
funct printANumber(int number, funct callbackFunction) {
    printout("The number you provided is: " + number);
}

// a function which we will use in a driver function as a callback function
funct printFinishMessage() {
    printout("I have finished printing numbers.");
}

// Driver method
funct event() {
   printANumber(6, printFinishMessage);
}

Resultado si llamaste al evento():

The number you provided is: 6
I have finished printing numbers.

El orden de salida aquí es importante. Dado que las funciones de devolución de llamada se llaman después, "Terminé de imprimir los números" se imprime al final, no al principio.

Las devoluciones de llamada reciben este nombre debido a su uso con lenguajes de puntero. Si no utiliza uno de esos, no se preocupe por el nombre "devolución de llamada". Simplemente comprenda que es solo un nombre para describir un método que se proporciona como argumento a otro método, de modo que cuando se llama al método principal (cualquiera que sea la condición, como un clic en un botón, un tic del temporizador, etc.) y se completa el cuerpo de su método, Luego se invoca la función de devolución de llamada.

Algunos lenguajes admiten construcciones donde se admiten múltiples argumentos de función de devolución de llamada y se llaman en función de cómo se completa la función principal (es decir, se llama a una devolución de llamada en caso de que la función principal se complete correctamente, se llama a otra en caso de que la función principal arroje un error específico, etc.).

8bitjunkie avatar Sep 26 '2011 01:09 8bitjunkie

Definición opaca

Una función de devolución de llamada es una función que usted proporciona a otro fragmento de código, lo que permite que ese código lo llame.

ejemplo artificial

Por qué querrías hacer esto? Digamos que hay un servicio que necesitas invocar. Si el servicio regresa inmediatamente, simplemente:

  1. Llámalo
  2. Espera el resultado
  3. Continúe una vez que llegue el resultado.

Por ejemplo, supongamos que el servicio fuera la factorialfunción. Cuando desee el valor de 5!, invocará factorial(5)y se producirán los siguientes pasos:

  1. Su ubicación de ejecución actual se guarda (en la pila, pero eso no es importante)

  2. La ejecución se entrega afactorial

  3. Cuando factorialse completa, coloca el resultado en algún lugar al que pueda acceder.

  4. La ejecución vuelve a donde estaba [1]

Ahora supongamos factorialque tomó mucho tiempo, porque le estás dando números enormes y necesita ejecutarse en algún clúster de supercomputación en algún lugar. Supongamos que espera que le lleve 5 minutos obtener el resultado. Tú podrías:

  1. Mantenga su diseño y ejecute su programa por la noche cuando esté dormido, para no estar mirando la pantalla la mitad del tiempo.

  2. Diseña tu programa para hacer otras cosas mientras factorialhace lo suyo

Si elige la segunda opción, las devoluciones de llamada podrían funcionar para usted.

Diseño de extremo a extremo

Para explotar un patrón de devolución de llamada, lo que desea es poder llamar factorialde la siguiente manera:

factorial(really_big_number, what_to_do_with_the_result)

El segundo parámetro, what_to_do_with_the_result, es una función que usted envía a factorial, con la esperanza de que factoriallo invoque según su resultado antes de regresar.

Sí, esto significa que factorialdebe haberse escrito para admitir devoluciones de llamadas.

Ahora suponga que desea poder pasar un parámetro a su devolución de llamada. Ahora no puedes, porque no vas a llamarlo factorial. Por lo tanto, factorialdebe escribirse para permitirle pasar sus parámetros y simplemente los entregará a su devolución de llamada cuando lo invoque. Podría verse así:

factorial (number, callback, params)
{
    result = number!   // i can make up operators in my pseudocode
    callback (result, params)
}

Ahora que factorialpermite este patrón, su devolución de llamada podría verse así:

logIt (number, logger)
{
    logger.log(number)
}

y tu llamada factorialsería

factorial(42, logIt, logger)

¿ Qué pasa si quieres devolver algo de logIt? Bueno, no puedes, porque factorialno le está prestando atención.

Bueno, ¿por qué no puedes factorialsimplemente devolver lo que devuelve tu devolución de llamada?

Haciéndolo sin bloqueo

Dado que la ejecución debe entregarse a la devolución de llamada cuando factorialfinalice, realmente no debería devolver nada a la persona que llama. E idealmente, de alguna manera iniciaría su trabajo en otro hilo/proceso/máquina y regresaría inmediatamente para que usted pueda continuar, tal vez algo como esto:

factorial(param_1, param_2, ...)
{
    new factorial_worker_task(param_1, param_2, ...);
    return;
}

Ahora es una "llamada asincrónica", lo que significa que cuando la llamas, regresa inmediatamente pero aún no ha hecho su trabajo. Por lo tanto, necesita mecanismos para verificarlo y obtener su resultado cuando esté terminado, y su programa se ha vuelto más complejo en el proceso.

Y, por cierto, al usar este patrón, factorial_worker_taskpuede iniciar su devolución de llamada de forma asincrónica y regresar inmediatamente.

Entonces, ¿Qué haces?

La respuesta es permanecer dentro del patrón de devolución de llamada. Cuando quieras escribir

a = f()
g(a)

y fdebe llamarse de forma asincrónica, en su lugar escribirá

f(g)

donde gse pasa como devolución de llamada.

Esto cambia fundamentalmente la topología de flujo de su programa y requiere algo de tiempo para acostumbrarse.

Su lenguaje de programación podría ayudarlo mucho al brindarle una forma de crear funciones sobre la marcha. En el código inmediatamente anterior, la función gpodría ser tan pequeña como print (2*a+1). Si su lenguaje requiere que defina esto como una función separada, con un nombre y una firma completamente innecesarios, entonces su vida se volverá desagradable si usa mucho este patrón.

Si, por otro lado, tu lenguaje te permite crear lambdas, entonces estás en mucho mejor forma. Luego terminarás escribiendo algo como

f( func(a) { print(2*a+1); })

que es mucho mejor.

Cómo pasar la devolución de llamada

¿ Cómo le pasarías la función de devolución de llamada factorial? Bueno, puedes hacerlo de varias maneras.

  1. Si la función llamada se ejecuta en el mismo proceso, puede pasar un puntero de función

  2. O tal vez quieras mantener un diccionario de fn name --> fn ptren tu programa, en cuyo caso podrías pasar el nombre

  3. Tal vez su idioma le permita definir la función en el lugar, ¡posiblemente como una lambda! Internamente se trata de crear algún tipo de objeto y pasar un puntero, pero no tienes que preocuparte por eso.

  4. Quizás la función a la que está llamando se esté ejecutando en una máquina completamente separada y la esté llamando mediante un protocolo de red como HTTP. Puede exponer su devolución de llamada como una función invocable por HTTP y pasar su URL.

Entiendes la idea.

El reciente aumento de las devoluciones de llamadas

En esta era de la web en la que hemos entrado, los servicios que invocamos suelen realizarse a través de la red. A menudo no tenemos ningún control sobre esos servicios, es decir, no los escribimos, no los mantenemos, no podemos asegurarnos de que estén funcionando o de su rendimiento.

Pero no podemos esperar que nuestros programas se bloqueen mientras esperamos que estos servicios respondan. Siendo conscientes de esto, los proveedores de servicios suelen diseñar API utilizando el patrón de devolución de llamada.

JavaScript admite muy bien las devoluciones de llamadas, por ejemplo, con lambdas y cierres. Y hay mucha actividad en el mundo de JavaScript, tanto en el navegador como en el servidor. Incluso se están desarrollando plataformas JavaScript para dispositivos móviles.

A medida que avancemos, cada vez más de nosotros escribiremos código asincrónico, por lo que esta comprensión será esencial.

Rohit Chatterjee avatar May 04 '2014 06:05 Rohit Chatterjee

La página de devolución de llamada en Wikipedia lo explica muy bien:

En programación de computadoras, una devolución de llamada es una referencia a un 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.

danio avatar May 05 '2009 10:05 danio

Explicación simple por analogía

Todos los días me pongo a trabajar. El jefe me dice:

Ah, y cuando hayas terminado con eso, tengo una tarea adicional para ti:

Excelente. Me entrega una nota con una tarea: esta tarea es una función de devolución de llamada. Podría ser cualquier cosa:

ben.doWork( and_when_finished_wash_my_car)

Mañana podría ser:

ben.doWork( and_tell_me_how_great_i_am)

¡El punto clave es que la devolución de la llamada debe realizarse DESPUÉS de que termine el trabajo! Haría bien en leer el código contenido en otras respuestas anteriores, que no necesito repetir.

BenKoshy avatar Feb 29 '2016 12:02 BenKoshy

Una respuesta sencilla sería que es una función que no la llama usted, sino el usuario o el navegador después de que haya ocurrido un determinado evento o después de que se haya procesado algún código.

NateH avatar Sep 15 '2011 16:09 NateH