Devolución de llamada (programación informática)

Una función que se pasa como datos a otra función y que será llamada por ella
Una devolución de llamada a menudo vuelve al nivel del llamador original.

En programación informática , una devolución de llamada es una función que se almacena como datos (una referencia ) y está diseñada para ser llamada por otra función, a menudo de vuelta a la capa de abstracción original .

Una función que acepta un parámetro de devolución de llamada puede estar diseñada para devolver la llamada antes de regresar a su llamador, lo que se conoce como sincrónica o bloqueante . La función que acepta una devolución de llamada puede estar diseñada para almacenar la devolución de llamada de modo que se pueda volver a llamar después de regresar, lo que se conoce como asincrónica , no bloqueante o diferida .

Los lenguajes de programación admiten devoluciones de llamadas de diferentes maneras, como punteros de función , expresiones lambda y bloques .

Analogía

Para ayudar a comprender el concepto, la siguiente es una analogía de la vida real.

Un cliente visita una tienda para hacer un pedido. Es como la primera llamada.

El cliente entrega al empleado una lista de artículos, un cheque para cubrir su costo e instrucciones de entrega. Estos son los parámetros de la primera llamada, incluida la devolución de la llamada, que son las instrucciones de entrega. Se entiende que se cobrará el cheque y que se seguirán las instrucciones.

Cuando el personal puede, entrega los artículos según las instrucciones, lo que es como llamar al servicio de devolución de llamada.

Cabe destacar que no es necesario que la entrega la realice el empleado que tomó el pedido. No es necesario que la función que aceptó la devolución de llamada como parámetro invoque una devolución de llamada.

Además, no es necesario que la entrega se realice directamente al cliente. No es necesario que una devolución de llamada se realice a la función que realiza la llamada. De hecho, una función generalmente no se haría pasar por una devolución de llamada. Algunos consideran que el uso de " back" es engañoso, ya que la llamada (generalmente) no se realiza al llamador original, como ocurre en el caso de una llamada telefónica .

Usar

Una devolución de llamada de bloqueo se ejecuta en el contexto de ejecución de la función que pasa la devolución de llamada. Una devolución de llamada diferida puede ejecutarse en un contexto diferente, como durante una interrupción o desde un subproceso . Como tal, una devolución de llamada diferida se puede utilizar para la sincronización y la delegación de trabajo a otro subproceso.

Manejo de eventos

Se puede utilizar una devolución de llamada para el manejo de eventos. A menudo, el código que se consume registra una devolución de llamada para un tipo particular de evento. Cuando se produce ese evento, se invoca la devolución de llamada.

Las devoluciones de llamadas se utilizan a menudo para programar la interfaz gráfica de usuario (GUI) de un programa que se ejecuta en un sistema de ventanas . La aplicación proporciona una referencia a una función de devolución de llamada personalizada para que la llame el sistema de ventanas. El sistema de ventanas llama a esta función para notificar a la aplicación eventos como clics del mouse y pulsaciones de teclas .

Acción asincrónica

Se puede utilizar una devolución de llamada para implementar el procesamiento asincrónico. El autor de la llamada solicita una acción y proporciona una devolución de llamada que se ejecutará cuando la acción se complete, lo que puede ocurrir mucho después de que se realice la solicitud.

Polimorfismo

Se puede utilizar una devolución de llamada para implementar el polimorfismo . En el siguiente pseudocódigo, SayHipuede tomar WriteStatuso WriteError.

def  WriteStatus ( string  mensaje ):  Write ( stdout ,  mensaje ) def  WriteError ( string  mensaje ):  Write ( stderr ,  mensaje ) def  SayHi ( escribir ):  write ( "Hola mundo" )

Acción condicional

Se puede utilizar una devolución de llamada para implementar un comportamiento condicional. En el siguiente pseudocódigo, si el registro está habilitado, Logse llama a la devolución de llamada getMessagey se escribe el resultado. Pero, si el registro no está habilitado, getMessageno se llama, lo que ahorra el costo de tiempo de ejecución.

def  Log ( getMessage ):  si  isLoggingEnabled :  mensaje  =  getMessage ();  WriteLine ( mensaje );

Implementación

La tecnología de devolución de llamada se implementa de forma diferente según el lenguaje de programación .

En lenguajes de ensamblaje , C , C++ , Pascal , Modula2 y otros, una función de devolución de llamada se almacena internamente como un puntero de función . El uso del mismo almacenamiento permite que distintos lenguajes compartan devoluciones de llamada directamente sin una capa de interoperabilidad en tiempo de diseño o de ejecución . Por ejemplo, la API de Windows es accesible a través de múltiples lenguajes, compiladores y ensambladores.

C++ también permite que los objetos proporcionen una implementación de la operación de llamada a una función. La biblioteca de plantillas estándar acepta estos objetos (llamados functores ) como parámetros.

Muchos lenguajes dinámicos , como JavaScript , Lua , Python , Perl [1] [2] y PHP , permiten pasar un objeto de función.

Los lenguajes CLI como C# y VB.NET proporcionan una referencia de función encapsulante de tipo seguro conocida como delegado .

Los eventos y los controladores de eventos , tal como se utilizan en los lenguajes .NET, permiten realizar devoluciones de llamadas.

Los lenguajes funcionales generalmente admiten funciones de primera clase , que pueden pasarse como devoluciones de llamadas a otras funciones, almacenarse como datos o devolverse desde funciones.

Muchos lenguajes, incluidos Perl, Python, Ruby , Smalltalk , C++ (11+), C# y VB.NET (nuevas versiones) y la mayoría de los lenguajes funcionales, admiten expresiones lambda , funciones sin nombre con sintaxis en línea, que generalmente actúan como devoluciones de llamadas.

En algunos lenguajes, incluidos Scheme , ML , JavaScript, Perl, Python, Smalltalk, PHP (desde 5.3.0), [3] C++ (11+), Java (desde 8), [4] y muchos otros, una lambda puede ser un cierre , es decir, puede acceder a variables definidas localmente en el contexto en el que se define la lambda.

En un lenguaje de programación orientado a objetos, como las versiones de Java anteriores a los argumentos con valores de función, el comportamiento de una devolución de llamada se puede lograr pasando un objeto que implementa una interfaz. Los métodos de este objeto son devoluciones de llamada.

En PL/I y ALGOL 60, un procedimiento de devolución de llamada puede necesitar poder acceder a variables locales en bloques contenedores, por lo que se lo llama a través de una variable de entrada que contiene tanto el punto de entrada como la información de contexto. [5]

Código de ejemplo

do

Las devoluciones de llamadas tienen una amplia variedad de usos, por ejemplo, en la señalización de errores: un programa Unix podría no querer terminar inmediatamente cuando recibe SIGTERM , por lo que para asegurarse de que su terminación se maneja correctamente, registraría la función de limpieza como una devolución de llamada. Las devoluciones de llamadas también se pueden usar para controlar si una función actúa o no: Xlib permite especificar predicados personalizados para determinar si un programa desea manejar un evento.

En el siguiente código C , la función PrintNumberutiliza el parámetro getNumbercomo una función de devolución de llamada de bloqueo. PrintNumberSe llama a la función con GetAnswerToMostImportantQuestionla que actúa como una función de devolución de llamada. Cuando se ejecuta, el resultado es: "Valor: 42".

#include <stdio.h> #include <stdlib.h>  void ImprimirNumero ( int ( * obtenerNumero )( void )) { int val = obtenerNumero (); printf ( "Valor: %d \n " , val ); }         int ObtenerRespuestaALaPreguntaMásImportante ( void ) { return 42 ; }    int main ( void ) { ImprimirNumero ( ObtenerRespuestaALaPreguntaMásImportante ); devolver 0 ; }     

C++

En C++, se puede utilizar functor además del puntero de función.

DO#

En el siguiente código C#Helper.Method , el método utiliza el parámetro callbackcomo una función de devolución de llamada de bloqueo. Helper.MethodSe llama con Logel cual actúa como una función de devolución de llamada. Cuando se ejecuta, se escribe lo siguiente en la consola: "La devolución de llamada fue: Hola mundo".

clase pública MainClass { static void Main ( string [] args ) { Helper helper = new Helper (); helper . Method ( Log ); }               void estático Log ( string str ) { Console.WriteLine ( $"La devolución de llamada fue: {str} " ) ; } }      clase pública Helper { método público void ( Acción < cadena > devolución de llamada ) { devolución de llamada ( "Hola mundo" ); } }         

Kotlin

En el siguiente código Kotlin , la función askAndAnswerusa el parámetro getAnswercomo una función de devolución de llamada de bloqueo. askAndAnswerSe llama con getAnswerToMostImportantQuestionla función que actúa como una función de devolución de llamada. Al ejecutarla, se le indicará al usuario que la respuesta a su pregunta es "42".

fun main () { print ( "Ingrese la pregunta más importante: " ) val question = readLine () askAndAnswer ( question , :: getAnswerToMostImportantQuestion ) }         divertido obtenerRespuestaALaPreguntaMásImportante (): Int { return 42 }     fun askAndAnswer ( pregunta : String?, obtenerRespuesta : ( ) -> Int ) { println ( "Pregunta: $ pregunta " ) println ( "Respuesta: ${ obtenerRespuesta () } " ) }         

JavaScript

En el siguiente código JavaScript , la función calculateusa el parámetro operatecomo una devolución de llamada de bloqueo. calculatese llama con multiplyy luego con sumque actúan como funciones de devolución de llamada.

función calcular ( a , b , operar ) { devolver operar ( a , b ); } función multiplicar ( a , b ) { devolver a * b ; } función suma ( a , b ) { devolver a + b ; } // genera 20 alerta ( calcular ( 10 , 2 , multiplicar )); // genera 12 alerta ( calcular ( 10 , 2 , suma ));                         

El método de colección .each()de la biblioteca jQuery utiliza la función que se le pasa como devolución de llamada de bloqueo. Llama a la devolución de llamada para cada elemento de la colección. Por ejemplo:

$ ( "li" ) .each ( función ( índice ) { console .log ( índice + ": " + $ ( this ) .text ( )); });      

Las devoluciones de llamadas diferidas se utilizan comúnmente para gestionar eventos del usuario, el cliente y los temporizadores. Se pueden encontrar ejemplos en addEventListener, Ajax y XMLHttpRequest. [6]

Además de utilizar devoluciones de llamadas en el código fuente de JavaScript, las funciones C que toman una función se admiten a través de js-ctypes. [7]

Rojo y REBOL

El siguiente código REBOL / Red demuestra el uso de devolución de llamada.

  • Como la alerta requiere una cadena, el formulario produce una cadena a partir del resultado del cálculo.
  • Los valores get-word! (es decir, :calc-product y :calc-sum) hacen que el intérprete devuelva el código de la función en lugar de evaluar con la función.
  • Las referencias de tipo de datos en un bloque [float! whole!] restringen el tipo de valores pasados ​​como argumentos.
Rojo  [ Título:  "Ejemplo de devolución de llamada" ]calcular:  func  [  num1  [ ¡número! ]  num2  [ ¡número! ]  función-de-devolución-de-llamada  [ ¡función! ] ][  función-de-devolución-de-llamada  num1  num2 ]calc-product:  func  [  num1  [ ¡número! ]  num2  [ ¡número! ] ][  num1  *  num2 ]calc-sum:  func  [  num1  [ ¡número! ]  num2  [ ¡número! ] ][  num1  +  num2 ]; alertas 75, el producto de 5 y 15 formulario de alerta  calcular 5 15 :calc-product    ; alertas 20, la suma de 5 y 15 formulario de alerta  calcular 5 15 :calc-sum    

Lua

Un ejemplo de interpolación de color que utiliza el motor Roblox que acepta una devolución de llamada .done opcional:

esperar ( 1 ) DT local  = esperar ()  función  tween_color ( objeto ,  color_final ,  tiempo_desvanecimiento )  local  paso_r  =  color_final.r - objeto.ColorFondo3.r local paso_g = color_final.g - objeto.ColorFondo3.g local paso_b = color_final.b - objeto.ColorFondo3.b local total_pasos = 1 / ( DT * ( 1 / tiempo_desvanecimiento ) ) local completado ;coroutine.wrap ( función ( ) para i = 0 , 1 , DT * ( 1 / tiempo_desvanecimiento ) hacer objeto.FondoColor3 = Color3.new ( objeto.FondoColor3.r + ( paso_r / pasos_totales ) , objeto.FondoColor3.g + ( paso_g / pasos_totales ) , objeto.FondoColor3.b + ( paso_b / pasos_totales ) ) esperar ( ) fin si se completó entonces se completó ( ) fin fin ) ( ) devolver { hecho = función ( devolución_de_llamada ) completado = devolución_de_llamada fin } fin                                                              tween_color ( some_object ,  Color3.new ( 1,0,0 ) , 1 ) .doin ( function ( ) print " ¡ Interpolación de color finalizada ! " end )     

Pitón

En el siguiente código Pythoncalculate , la función acepta un parámetro operateque se utiliza como una devolución de llamada de bloqueo. calculatese llama con squareque actúa como una función de devolución de llamada.

def  cuadrado ( val ):  devuelve  val  **  2 def  calcular ( operar ,  val ):  devuelve  operar ( val ) # salidas: 25 calcular ( cuadrado ,  5 )

Julia

En el siguiente código de Juliacalculate , la función acepta un parámetro operateque se utiliza como una devolución de llamada de bloqueo. calculatese llama con squareque actúa como una función de devolución de llamada.

julia> cuadrado ( val ) = val ^ 2 cuadrado (función genérica con 1 método) julia> calcular ( operar , val ) = operar ( val ) calcular (función genérica con 1 método) julia> calcular ( cuadrado , 5 ) 25       

Véase también

Referencias

  1. ^ "Perl Cookbook - 11.4. Toma de referencias a funciones". 2 de julio de 1999. Consultado el 3 de marzo de 2008 .
  2. ^ "Programación avanzada en Perl - 4.2 Uso de referencias de subrutinas". 2 de julio de 1999. Consultado el 3 de marzo de 2008 .
  3. ^ "Referencia del lenguaje PHP - Funciones anónimas" . Consultado el 8 de junio de 2011 .
  4. ^ "Novedades en JDK 8". oracle.com .
  5. ^ Belzer, Jack; Holzman, Albert G; Kent, Allen, eds. (1979). Enciclopedia de informática y tecnología: volumen 12. Marcel Dekker, inc. pág. 164. ISBN 0-8247-2262-0. Recuperado el 28 de enero de 2024 .
  6. ^ "Creación de devoluciones de llamadas de JavaScript en componentes". Archivo. UDN Web Docs (página de documentación). sec. Funciones de JavaScript como devoluciones de llamadas. Archivado desde el original el 2021-12-16 . Consultado el 2021-12-16 .
  7. ^ Holley, Bobby; Shepherd, Eric (eds.). "Declaración y uso de devoluciones de llamadas". Docs. Mozilla Developer Network (página de documentación). Archivado desde el original el 17 de enero de 2019. Consultado el 16 de diciembre de 2021 .
  • Instintos básicos: Implementación de notificaciones de devolución de llamadas mediante delegados - MSDN Magazine , diciembre de 2002
  • Implementar rutinas de devolución de llamada en Java
  • Implementación de un marco de devolución de llamadas de script en ASP.NET 1.x - Proyecto de código, 2 de agosto de 2004
  • Interfaz de funciones miembro de C++ con bibliotecas de C (archivado desde el original el 6 de julio de 2011)
  • Caso práctico de estilo n.° 2: devoluciones de llamadas genéricas
Obtenido de "https://es.wikipedia.org/w/index.php?title=Devolución_de_llamada_(programación_informática)&oldid=1245692165"