
En programación informática , una función de devolución de llamada (callback) es un patrón de programación en el que se pasa una referencia a una función de un contexto (consumidor) a otro (proveedor) para que este último pueda llamar a la función. Si la función accede al estado o la funcionalidad del consumidor, la llamada se realiza de vuelta al consumidor, en sentido inverso al flujo de control habitual en el que un consumidor llama a un proveedor.
Una función que acepta un parámetro de devolución de llamada puede diseñarse para devolver la llamada antes de regresar a quien la llamó. Sin embargo, lo más habitual es que el proveedor almacene una referencia a la devolución de llamada para poder llamar a la función posteriormente (como diferida ). Si el proveedor invoca la devolución de llamada en el mismo hilo que el consumidor, la llamada es síncrona o bloqueante . Si, en cambio, el proveedor invoca la devolución de llamada en un hilo diferente, la llamada es asíncrona o no bloqueante .
Una devolución de llamada se puede comparar con dejar instrucciones a un sastre sobre qué hacer cuando un traje esté listo, como llamar a un número de teléfono específico o entregarlo en una dirección determinada. Estas instrucciones representan una devolución de llamada: una función proporcionada con antelación para ser ejecutada posteriormente, a menudo por una parte diferente del sistema y no necesariamente por la que la recibió.
La diferencia entre una referencia a una función general y una devolución de llamada puede ser sutil, y algunos usan los términos indistintamente, pero la distinción generalmente depende de la intención de programación. Si la intención es similar a la de una devolución de llamada telefónica —que la persona que recibió la llamada original se comunique con quien llamó originalmente— entonces se trata de una devolución de llamada.
Usar
Una función de devolución de llamada bloqueante se ejecuta en el contexto de ejecución de la función que la recibe. Una función de devolución de llamada diferida puede ejecutarse en un contexto diferente, como durante una interrupción o desde un hilo . Por lo tanto, una función de devolución de llamada diferida puede utilizarse para la sincronización y la delegación de tareas a otro hilo.
Gestión de eventos
Una función de devolución de llamada se puede usar para gestionar eventos. A menudo, el código que consume la función registra una función de devolución de llamada para un tipo de evento específico. Cuando ocurre dicho evento, se llama a la función de devolución de llamada. Las funciones de devolución de llamada se usan frecuentemente 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 el sistema de ventanas la llame. El sistema de ventanas llama a esta función para notificar a la aplicación sobre eventos como clics del ratón y pulsaciones de teclas .
acción asíncrona
Se puede utilizar una función de devolución de llamada para implementar el procesamiento asíncrono.
Quien realiza la llamada solicita una acción y proporciona una función de devolución de llamada que se ejecutará cuando la acción finalice, lo cual puede ocurrir mucho tiempo después de realizada la solicitud.
Polimorfismo
Se puede utilizar una función de devolución de llamada para implementar el polimorfismo . En el siguiente pseudocódigo, say_hipuede tomar write_statuso write_error.
from typing import Callabledef write_status ( mensaje : str ) -> None : write ( stdout , mensaje )def write_error ( mensaje : str ) -> None : write ( stderr , mensaje )def say_hi ( write : Callable [[ str ], None ]) -> None : write ( "Hola mundo" )Implementación
La tecnología de devolución de llamada se implementa de manera diferente según el lenguaje de programación .
En lenguaje ensamblador , C , C++ , Pascal , Modula-2 y otros, una función de devolución de llamada se almacena internamente como un puntero a función [ 1 ] . El uso del mismo almacenamiento permite que diferentes lenguajes compartan directamente las devoluciones de llamada sin una capa de interoperabilidad en tiempo de diseño o tiempo 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 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 [ 2 ] [ 3 ] y PHP , permiten que se pase un objeto de función. Los lenguajes CLI como C# y Visual Basic (.NET) (VB.NET) proporcionan una referencia de función encapsuladora segura en tipos conocida como delegado . Los eventos y los controladores de eventos , como se usan en los lenguajes .NET , proporcionan devoluciones de llamada. Los lenguajes de programación funcional generalmente admiten funciones de primera clase , que pueden pasarse como funciones de devolución de llamada a otras funciones, almacenarse como datos o devolverse desde funciones.
Muchos lenguajes, incluidos Perl, Python, Ruby , Smalltalk , C++ (11+), C# y VB.NET (versiones nuevas) y la mayoría de los lenguajes funcionales, admiten expresiones lambda , funciones sin nombre con sintaxis en línea, que generalmente actúan como callbacks. En algunos lenguajes, incluidos Scheme , ML , JavaScript, Perl, Python, Smalltalk, PHP (desde 5.3.0), [ 4 ] C++ (11+), Java (desde 8), [ 5 ] 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 valor de función, el comportamiento de un callback se puede lograr pasando un objeto que implementa una interfaz. Los métodos de este objeto son callbacks. En PL/I y ALGOL 60 , un procedimiento de devolución de llamada puede necesitar acceder a variables locales en bloques contenedores, por lo que se llama a través de una variable de entrada que contiene tanto el punto de entrada como la información de contexto. [ 6 ]
Código de ejemplo
do
Las funciones de devolución 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 gestione correctamente, registraría la función de limpieza como una función de devolución de llamada.
#include <signal.h> #include <stdlib.h>#include <fcntl.h> #include <sys/types>struct Data { // ... };static struct Data data = { // inicializar el contenido aquí }; static volatile sigatomic_t flag = 0 ;void cleanupHandler ( int signum ) { flag = 1 ; int saveFile = open ( "savefile.dat" , O_WRONLY | O_CREATO , S_IWUSR ); write ( saveFile , data , sizeof data ); close ( saveFile ); }int main ( void ) { signal ( cleanupHandler , SIGTERM ); doStuff (); }Otro uso común de las funciones de devolución de llamada en C es con las funciones de la biblioteca estándar de C utilizadas para ordenar ( qsort()) y buscar ( lsearch(), bsearch()) donde se pasa una función comparadora como argumento a la rutina para determinar el orden de intercalación. [ 7 ]
#include <stdlib.h>struct Persona { char nombre [ 20 ]; int edad ; };Persona personas [ 1000 ];// Compara personas por edad int compareAges ( struct Person * p1 , struct Person * p2 ) { return p2 -> age - p1 -> age ; }int main ( void ) { // ... int numberOfPeople = /* algún número aquí */ ;qsort ( numberOfPeople , sizeof ( struct Person ), people , compareAges ); // ... }Las funciones de devolución 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. En el siguiente código CprintNumber() , la función usa un parámetro getNumbercomo una función de devolución de llamada bloqueante. printNumber()Se llama con getAnswerToMostImportantQuestion()que actúa como una función de devolución de llamada. Cuando se ejecuta, la respuesta a la pregunta es: "Valor: 42".
#include <stdio.h> #include <stdlib.h>void printNumber ( int ( * getNumber )( void )) { int val = getNumber (); printf ( "Valor: %d \n " , val ); }int obtenerRespuestaALaPreguntaMásImportante ( void ) { return 42 ; }int main ( void ) { printNumber ( getAnswerToMostImportantQuestion ); return 0 ; }C++
En C++ , los functores se pueden usar además de los punteros a funciones. Un functor es un objeto con operator()un identificador definido. Por ejemplo, los objetos en std::viewsson functores. Este es un ejemplo de uso de functores en C++:
importar std ;clase MyCallback { public : void operator ()( int x ) { std :: println ( "Callback llamado con valor: {}" , x ); } };template < typename Callback > void performOperation ( int a , Callback callback ) { std :: println ( "Realizando operación en: {}" , a ); callback ( a ); }int main () { MyCallback callback ; int value = 10 ; performOperation ( value , callback ); return 0 ; }std::function<R(Args...)>es un contenedor sin tipo para cualquier objeto invocable, introducido en C++11 :
importar std ;usando std :: función ;void performOperation ( int a , function < void ( int ) > callback ) { std :: println ( "Realizando operación en: {}" , a ); callback ( a ); }int main () { int value = 10 ; performOperation ( value , []( int x ) -> void { std :: println ( "Callback llamado con valor: {}" , x ); });devolver 0 ; }DO#
En el siguiente código C#Helper.PerformAction , el método utiliza un parámetro callbackcomo una función de devolución de llamada bloqueante. Helper.PerformActionSe llama con Logque 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".
espacio de nombres Wikipedia.Ejemplos ;usando el sistema ;clase Helper { public void PerformAction ( Action < string > callback ) { callback ( "Hola mundo" ); } }public class Main { static void Log ( string str ) { Console . WriteLine ( $"Callback fue: {str}" ); }static void Main ( string [ ] args ) { Helper helper = new (); helper.PerformAction ( Log ) ; } }JavaScript
En el siguiente código JavaScriptcalculate , la función utiliza un parámetro operatecomo una función de devolución de llamada bloqueante. calculateSe llama con multiplyy luego con sumque actúan como funciones de devolución de llamada.
function calculate ( a , b , operate ) { return operate ( a , b ); } function multiply ( a , b ) { return a * b ; } function sum ( a , b ) { return a + b ; } // muestra 20 alert ( calculate ( 10 , 2 , multiply )); // muestra 12 alert ( calculate ( 10 , 2 , sum ));El método `collection` .each()de la biblioteca jQuery utiliza la función que se le pasa como una función de devolución de llamada bloqueante. Llama a la función de devolución de llamada para cada elemento de la colección. Por ejemplo:
$ ( "li" ). each ( function ( index ) { console . log ( index + ": " + $ ( this ). text ()); });Las devoluciones de llamada diferidas se utilizan comúnmente para manejar eventos del usuario, el cliente y los temporizadores. Se pueden encontrar ejemplos en addEventListener, Ajax y XMLHttpRequest. [ 8 ]
Además de usar callbacks en el código fuente de JavaScript, las funciones de C que aceptan una función son compatibles a través de js-ctypes. [ 9 ]
Julia
En el siguiente código Julia , la función calculateacepta un parámetro operateque se utiliza como una función de devolución de llamada bloqueante. calculateSe llama con squareque actúa como una función de devolución de llamada.
julia> square ( val ) = val ^ 2 square (función genérica con 1 método) julia> calculate ( operate , val ) = operate ( val ) calculate (función genérica con 1 método) julia> calculate ( square , 5 ) 25Kotlin
En el siguiente código Kotlin , la función askAndAnswerutiliza un parámetro getAnswercomo una función de devolución de llamada bloqueante. askAndAnswerSe llama con getAnswerToMostImportantQuestionque actúa como una función de devolución de llamada. Cuando se ejecuta, la respuesta a la pregunta es: "42".
fun main () { print ( "Ingrese la pregunta más importante: " ) val question = readLine () askAndAnswer ( question , :: getAnswerToMostImportantQuestion ) }fun getAnswerToMostImportantQuestion (): Int { return 42 }fun askAndAnswer ( question : String? , getAnswer : () -> Int ) { println ( "Pregunta: $ question " ) println ( "Respuesta: ${ getAnswer () } " ) }Lua
En este código Lua , la función calculateacepta el operationparámetro que se utiliza como una función de devolución de llamada bloqueante. calculateSe llama con ambos addy multiply, y luego utiliza una función anónima para dividir.
función calcular ( a , b , operación ) devolver operación ( a , b ) finfunción multiplicar ( a , b ) devolver a * b finfunción agregar ( a , b ) devolver a + b finprint ( calculate ( 10 , 20 , multiply )) -- muestra 200 print ( calculate ( 10 , 20 , add )) -- muestra 30 -- un ejemplo de una función de devolución de llamada usando una función anónima print ( calculate ( 10 , 20 , function ( a , b ) return a / b -- muestra 0.5 end ))Pitón
En el siguiente código Python , la función calculateacepta un parámetro operateque se utiliza como una función de devolución de llamada bloqueante. calculateSe llama con squareque actúa como una función de devolución de llamada.
def cuadrado ( val : int ) -> int : return val ** 2def calcular ( operar : Llamable [[ int ], int ], val : int ) -> int : return operar ( val )# impresiones: 25 imprimir ( calcular ( cuadrado , 5 ))Rebol, Rojo
El siguiente código de Rebol y Red demuestra el uso de funciones de devolución de llamada.
- Como alert requiere una cadena, form produce una cadena a partir del resultado de calculate.
- 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 evaluarla con la función.
- Las referencias de tipo de datos en un bloque [float! integer!] 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-de-de-llamada [ función! ] ][ función-de-de-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-producto; alertas 20, la suma de 5 y 15 formulario de alerta calcular 5 15 :calc-sumÓxido
Rust tiene los Fnrasgos FnMuty FnOnce. [ 10 ]
fn call_with_one < F > ( func : F ) -> usize donde F : Fn ( usize ) -> usize { func ( 1 ) }let double = | x | x * 2 ; assert_eq! ( call_with_one ( double ), 2 );Véase también
- Patrón de comando
- Estilo de pase de continuación
- Bucle de eventos
- Programación basada en eventos
- Invocación implícita
- Inversión del control
- libsigc++ , una biblioteca de devolución de llamada para C++.
- Señales y ranuras
- Salida del usuario
Referencias
- ↑ Gallaba, K., Mesbah, A., & Beschastnikh, I. (2015). No nos llames, te llamaremos nosotros: Caracterización de las funciones de devolución de llamada en JavaScript. Simposio Internacional ACM/IEEE de 2015 sobre Ingeniería de Software Empírica y Medición (ESEM) . https://doi.org/10.1109/esem.2015.7321196
- ↑ "Perl Cookbook - 11.4. Tomando referencias a funciones" . 2 de julio de 1999. Consultado el 3 de marzo de 2008 .
- ↑ "Programación avanzada en Perl - 4.2 Uso de referencias a subrutinas" . 2 de julio de 1999. Consultado el 3 de marzo de 2008 .
- ↑ "Referencia del lenguaje PHP – Funciones anónimas" . Consultado el 8 de junio de 2011 .
- ↑ "Novedades de JDK 8" . oracle.com .
- ↑ Belzer, Jack; Holzman, Albert G; Kent, Allen, eds. (1979). Enciclopedia de Ciencias de la Computación y Tecnología: Volumen 12. Marcel Dekker, Inc. pág. 164. ISBN 0-8247-2262-0Consultado el 28 de enero de 2024 .
- ↑ Prinz, Peter; Crawford, Tony (12 de diciembre de 2015). C en pocas palabras (2.ª ed.). Sebastopol, California: O'Reilly Media Inc. págs. 538–540 , 374–377 . ISBN 978-1-491-90475-6.
- ↑ "Creación de callbacks de JavaScript en componentes" . Archivo. UDN Web Docs (página de documentación). sec. Funciones de JavaScript como callbacks. Archivado del original el 16/12/2021 . Consultado el 16/12/2021 .
- ↑ Holley, Bobby; Shepherd, Eric (eds.). "Declaración y uso de funciones de devolución de llamada" . Docs. Mozilla Developer Network (página de documentación). Archivado del original el 17 de enero de 2019. Consultado el 16 de diciembre de 2021 .
- ↑ "Fn en std::ops - Rust" . doc.rust-lang.org . Consultado el 18 de enero de 2025 .
Enlaces externos
- Instintos básicos: Implementación de notificaciones de devolución de llamada mediante delegados - Revista MSDN , diciembre de 2002
- Implementar rutinas de devolución de llamada en Java
- Implementación del marco de devolución de llamada de scripts en ASP.NET 1.x - Code Project, 2 de agosto de 2004
- Interconexión de funciones miembro de C++ con bibliotecas de C (archivado del original el 6 de julio de 2011)
- Caso práctico de estilo n.° 2: Devoluciones de llamada genéricas
- Subrutinas