De Wikipedia, la enciclopedia libre
Saltar a navegación Saltar a búsqueda
A menudo, una devolución de llamada vuelve al nivel de la persona que llamaba originalmente.

En la programación de computadoras , una devolución de llamada , también conocido como un " call-después " [1] función , es cualquier código ejecutable que se pasa como argumento a otro código; se espera que otro código vuelva a llamar (ejecutar) el argumento en un momento dado. Esta ejecución puede ser inmediata como en una devolución de llamada sincrónica , o puede ocurrir en un momento posterior como en una devolución de llamada asincrónica . Los lenguajes de programación admiten devoluciones de llamada de diferentes maneras, a menudo implementándolas con subrutinas , expresiones lambda , bloques, o punteros de función .

Diseño [ editar ]

Hay dos tipos de devoluciones de llamada, que se diferencian en cómo controlan el flujo de datos en tiempo de ejecución: las devoluciones de llamada bloqueadas (también conocidas como devoluciones de llamada síncronas o simplemente devoluciones de llamada ) y las devoluciones de llamada diferidas (también conocidas como devoluciones de llamada asincrónicas ). Mientras que las devoluciones de llamada de bloqueo se invocan antes de que regrese una función (en el ejemplo de C a continuación, que ilustra una devolución de llamada de bloqueo, es la función principal), se pueden invocar devoluciones de llamada diferidas después de que regrese una función. Las devoluciones de llamada diferidas se usan a menudo en el contexto de operaciones de E / S o manejo de eventos, y son llamadas por interrupciones o por un subproceso diferente en el caso de varios subprocesos. Debido a su naturaleza, el bloqueo de devoluciones de llamada puede funcionar sin interrupciones o múltiples hilos, lo que significa que el bloqueo de devoluciones de llamada no se usa comúnmente para sincronizar o delegar trabajo a otro hilo.

Las devoluciones de llamada se utilizan para programar aplicaciones en sistemas de ventanas . En este caso, la aplicación proporciona (una referencia a) una función de devolución de llamada personalizada específica para que el sistema operativo la llame, que luego llama a esta función específica de la aplicación en respuesta a eventos como clics del mouse o pulsaciones de teclas. Una de las principales preocupaciones aquí es la gestión de los privilegios y la seguridad: aunque la función se llama desde el sistema operativo, no debería ejecutarse con los mismos privilegios que el sistema. Una solución a este problema es utilizar anillos de protección.

Implementación [ editar ]

La forma de una devolución de llamada varía entre los lenguajes de programación :

  • En ensamblador , C , C ++ , Pascal , Modula2 y lenguajes similares, se puede pasar un puntero a nivel de máquina a una función como argumento a otra función (interna o externa). Esto es compatible con la mayoría de los compiladores y proporciona la ventaja de usar diferentes lenguajes juntos sin bibliotecas o clases especiales de envoltura. Un ejemplo puede ser la API de Windows que es directamente (más o menos) accesible por muchos lenguajes, compiladores y ensambladores diferentes.
  • C ++ permite que los objetos proporcionen su propia implementación de la operación de llamada a la función. La biblioteca de plantillas estándar acepta estos objetos (llamados functores ), así como punteros de función, como parámetros para varios algoritmos polimórficos.
  • Muchos lenguajes dinámicos , como JavaScript , Lua , Python , Perl [2] [3] y PHP , simplemente permiten pasar un objeto de función.
  • Los lenguajes CLI como C # y VB.NET proporcionan una referencia encapsulada de tipo seguro , un " delegado ", para definir punteros de función bien tipificados . Estos se pueden utilizar como devoluciones de llamada.
  • Los eventos y los controladores de eventos , tal como se utilizan en los lenguajes .NET, proporcionan una sintaxis generalizada para las devoluciones de llamada.
  • Los lenguajes funcionales generalmente admiten funciones de primera clase , que pueden pasarse como devoluciones de llamada a otras funciones, almacenarse como datos o devolverse desde funciones.
  • Algunos lenguajes, como Algol 68 , Perl, Python, Ruby , Smalltalk , C ++ 11 y posteriores, las versiones más recientes de C # y VB.NET, así como la mayoría de los lenguajes funcionales, permiten que se proporcionen bloques de código sin nombre ( expresiones lambda ) en lugar de referencias a funciones definidas en otro lugar.
  • En algunos lenguajes, por ejemplo, Scheme , ML , JavaScript, Perl, Python, Smalltalk, PHP (desde 5.3.0), [4] C ++ 11 y posteriores, Java (desde 8), [5] y muchos otros, tales funciones pueden ser cierres , es decir, pueden acceder y modificar variables definidas localmente en el contexto en el que se definió la función. Sin embargo, tenga en cuenta que Java no puede modificar las variables locales en el ámbito adjunto.
  • En lenguajes de programación orientados a objetos sin argumentos con valores de función, como en Java antes de su versión 8, las devoluciones de llamada se pueden simular pasando una instancia de una clase o interfaz abstracta, de la cual el receptor llamará a uno o más métodos, mientras que la llamada end proporciona una implementación concreta. Estos objetos son efectivamente un conjunto de devoluciones de llamada, más los datos que necesitan manipular [ aclaración necesaria ] . Son útiles para implementar varios patrones de diseño , como Visitante , Observador y Estrategia .

Utilice [ editar ]

C [ editar ]

Las devoluciones de llamada 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 llamada 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.

El siguiente código C demuestra el uso de devoluciones de llamada para mostrar dos números.

#include  <stdio.h>#include  <stdlib.h>/ * La función de llamada toma una única devolución de llamada como parámetro. * / void  PrintTwoNumbers ( int  ( * numberSource ) ( void ))  {  int  val1  =  numberSource ();  int  val2  =  numberSource ();  printf ( "% d y% d \ n " ,  val1 ,  val2 ); }/ * Una posible devolución de llamada * / int  overNineThousand ( void )  {  return  ( rand () % 1000 )  +  9001 ; }/ * Otra posible devolución de llamada. * / int  MeaningOfLife ( void )  {  return  42 ; }/ * Aquí llamamos PrintTwoNumbers () con tres devoluciones de llamada diferentes. * / int  main ( vacío )  {  time_t  t ;  srand (( sin firmar ) hora ( & t ));  // Inicializar semilla para la función aleatoria  PrintTwoNumbers ( & rand );  PrintTwoNumbers ( & overNineThousand );  PrintTwoNumbers ( & MeaningOfLife );  return  0 ; }

Salida de ejemplo:

2895 y 235739272 y 998242 y 42

Observe cómo esto es diferente de simplemente pasar la salida de la función de devolución de llamada a la función de llamada, PrintTwoNumbers () - en lugar de imprimir el mismo valor dos veces, PrintTwoNumbers llama a la devolución de llamada tantas veces como sea necesario. Esta es una de las dos ventajas principales de las devoluciones de llamada.

La otra ventaja es que la función que llama puede pasar los parámetros que desee a las funciones llamadas (no se muestra en el ejemplo anterior). Esto permite ocultar la información correctamente : el código que pasa una devolución de llamada a una función que llama no necesita conocer los valores de los parámetros que se pasarán a la función. Si solo pasa el valor de retorno, entonces los parámetros deberían exponerse públicamente. [ ejemplo necesario ]

Otro ejemplo:

/ * * Este es un programa C simple para demostrar el uso de devoluciones de llamada * La función de devolución de llamada está en el mismo archivo que el código de llamada. * La función de devolución de llamada se puede colocar más tarde en una biblioteca externa como * por ejemplo, un objeto compartido para aumentar la flexibilidad. * * /#include  <stdio.h>#include  <string.h>typedef  struct  _MyMsg  {  int  appId ;  char  msgbody [ 32 ]; }  MyMsg ;void  myfunc ( MyMsg  * msg ) {  if  ( strlen ( msg -> msgbody )  >  0  )  printf ( "App Id =% d \ n Msg =% s \ n " , msg -> appId ,  msg -> msgbody );  else  printf ( "App Id =% d \ n Msg = No Msg \ n " , msg -> appId ); }/ * * Declaración de prototipo * / void  ( * devolución de llamada ) ( MyMsg  * );int  main ( void ) {  MyMsg  msg1 ;  msg1 . appId  =  100 ;  strcpy ( msg1 . msgbody ,  "Esto es una prueba \ n " ); / *  * Asignar la dirección de la función "myfunc" a la función  * puntero "callback" (también puede escribirse como "callback = & myfunc;")  * /  callback  =  myfunc ; / *  * Llamar a la función (también puede escribirse como "(* devolución de llamada) (& msg1);")  * /  devolución de llamada ( & msg1 ); return  0 ; }

La salida después de la compilación:

$ gcc cbtest.c $ ./a.out App Id = 100 Msg = Esto es una prueba

Esta ocultación de información significa que las devoluciones de llamada se pueden utilizar al comunicarse entre procesos o subprocesos, o mediante comunicaciones serializadas y datos tabulares. [ aclaración necesaria ]

En C ++, el functor también se usa comúnmente junto al uso del puntero de función en C.

C # [ editar ]

Una simple devolución de llamada en C # :

 clase  pública Class1  {  static  void  Main ( string []  args )  {  Class2  c2  =  new  Class2 ();  / *  * Método de llamada en Class2 con método de devolución de llamada como parámetro  * /  c2 . Método ( CallBackMethod );  } / *  * El método de devolución de llamada. Este método imprime la cadena enviada en la devolución de llamada  * /  static  void  CallBackMethod ( string  str )  {  Console . WriteLine ( $ "La devolución de llamada era: {str}" );  } }public  class  Class2 {  / *  * El método que devuelve la llamada a la persona que llama. Toma una acción (método) como parámetro  * /  public  void  Method ( Action < string >  callback )  {  / *  * Vuelve a llamar al método CallBackMet en Class1 con el mensaje especificado  * /  callback ( "El mensaje a enviar" );  } }

JavaScript [ editar ]

Las devoluciones de llamada se utilizan en la implementación de lenguajes como JavaScript , incluido el soporte de funciones de JavaScript como devoluciones de llamada a través de js-ctypes [6] y en componentes como addEventListener. [7] Sin embargo, se puede escribir un ejemplo nativo de devolución de llamada sin ningún código complejo:

función  calcular ( num1 ,  num2 ,  callbackFunction )  {  return  callbackFunction ( num1 ,  num2 ); }function  calcProduct ( num1 ,  num2 )  {  return  num1  *  num2 ; }function  calcSum ( num1 ,  num2 )  {  return  num1  +  num2 ; } // alertas 75, el producto de 5 y 15 alert ( calcular ( 5 ,  15 ,  calcProduct )); // alerta 20, la suma de 5 y 15 alerta ( calcular ( 5 ,  15 ,  calcSum ));

Primero, se define una función de cálculo con un parámetro destinado a la devolución de llamada: callbackFunction . Luego, se define una función que se puede usar como devolución de llamada para calcular , calcProduct . Se pueden usar otras funciones para callbackFunction , como calcSum . En este ejemplo, calculate () se invoca dos veces, una con calcProduct como devolución de llamada y otra con calcSum . Las funciones devuelven el producto y la suma, respectivamente, y luego la alerta los mostrará en la pantalla.

En este ejemplo primitivo, el uso de una devolución de llamada es principalmente una demostración de principio. Uno podría simplemente llamar a las devoluciones de llamada como funciones regulares, calcProduct (num1, num2) . Las devoluciones de llamada se utilizan generalmente cuando la función necesita realizar eventos antes de que se ejecute la devolución de llamada, o cuando la función no tiene (o no puede) tener valores de retorno significativos sobre los que actuar, como es el caso de JavaScript asíncrono (basado en temporizadores) o solicitudes XMLHttpRequest . Se pueden encontrar ejemplos útiles en bibliotecas de JavaScript como jQuery, donde el método .each () itera sobre un objeto similar a una matriz, siendo el primer argumento una devolución de llamada que se realiza en cada iteración.

Red y REBOL [ editar ]

Desde el JavaScript anterior, así es como se implementaría el mismo en REBOL o Red (lenguaje de programación) . Observe la presentación más limpia de los datos como código.

  • return está implícito ya que el código en cada función es la última línea del bloque
  • Como la alerta requiere una cadena, el formulario produce una cadena a partir del resultado de calcular
  • ¡El get-word! Los valores (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.
  • ¡El tipo de datos! referencias en un bloque! [¡flotador! integer!] restringe 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-producto:  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 forma de alerta  calcular 5 15 : calc-producto    ; alertas 20, la suma de 5 y 15 forma de alerta  calcula 5 15 : suma de cálculo    

Lua [ editar ]

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

esperar ( 1 ) DT local  = esperar ()  función  tween_color ( objeto ,  finish_color ,  fade_time )  local  step_r  =  finish_color . r  -  objeto . BackgroundColor3 . r  local  step_g  =  finish_color . g  -  objeto . BackgroundColor3 . g  local  step_b  =  finish_color . b  -  objeto . BackgroundColor3 . b  pasos_total locales  =  1 / ( DT * ( 1 / fade_time ))  local  completado ;  coroutine.wrap ( function ()  para  i  =  0 ,  1 ,  DT * ( 1  /  fade_time )  do  object . BackgroundColor3  =  Color3 . new  (  object . BackgroundColor3 . r  +  ( step_r / total_steps ),  object. BackgroundColor3 . g  +  ( paso_g / total_pasos ),  objeto . BackgroundColor3 . b  +  ( step_b / total_steps )  )  esperar ()  finalizar  si se  completó  luego  completado ()  finalizar  finalizar ) ()  volver  {  hecho  =  función ( devolución de llamada )  completado  =  devolución de llamada  final  } finaltween_color ( algún_objeto ,  Color3 . nuevo ( 1 ,  0 ,  0 ),  1 ). done ( function ()  print  "¡Interpolación de colores terminada!" end )

Python [ editar ]

Un uso clásico de las devoluciones de llamada en Python (y otros lenguajes) es asignar eventos a los elementos de la interfaz de usuario.

Aquí hay un ejemplo muy trivial del uso de una devolución de llamada en Python. Primero defina dos funciones, la devolución de llamada y el código de llamada, luego pase la función de devolución de llamada al código de llamada.

>>> def  get_square ( val ): ...  "" "La devolución de llamada." "" ...  return  val  **  2 ... >>> def  caller ( func ,  val ): ...  return  func ( val ) ... >>> llamador ( get_square ,  5 ) 25

Ver también [ editar ]

  • Patrón de comando
  • Estilo de continuación-pase
  • Bucle de eventos
  • Programación impulsada por eventos
  • Invocación implícita
  • Inversión de control
  • libsigc ++ , una biblioteca de devolución de llamada para C ++
  • Señales y ranuras
  • Salida de usuario

Referencias [ editar ]

  1. ^ "¿Qué es una función de devolución de llamada?" . Desbordamiento de pila . Consultado el 16 de mayo de 2018 .
  2. ^ "Libro de cocina de Perl - 11.4. Tomando referencias a funciones" . Consultado el 3 de marzo de 2008 . CS1 maint: parámetro desalentado ( enlace )
  3. ^ "Programación avanzada de Perl - 4.2 usando referencias de subrutinas" . Consultado el 3 de marzo de 2008 . CS1 maint: parámetro desalentado ( enlace )
  4. ^ "Referencia del lenguaje PHP - Funciones anónimas" . Consultado el 8 de junio de 2011 . CS1 maint: parámetro desalentado ( enlace )
  5. ^ "Novedades de JDK 8" . oracle.com .
  6. ^ "Devolución de llamada" . Red de desarrolladores de Mozilla . Consultado el 13 de diciembre de 2012 . CS1 maint: parámetro desalentado ( enlace )
  7. ^ "Creación de devoluciones de llamada de Javascript en componentes" . Red de desarrolladores de Mozilla . Consultado el 13 de diciembre de 2012 . CS1 maint: parámetro desalentado ( enlace )

Enlaces externos [ editar ]

  • Instintos básicos: implementación de notificaciones de devolución de llamada mediante delegados
  • Implementar rutinas de devolución de llamada en Java
  • Implementar Script Callback Framework en ASP.NET
  • Interfaz de funciones miembro de C ++ con bibliotecas de C (archivado desde el original el 6 de julio de 2011)
  • Estudio de caso de estilo n. ° 2: devoluciones de llamada genéricas