Retrollamada (informática)

En programación informática, una retrollamada o devolución de llamada (en inglés: callback)[1] es una función ejecutable «A» que se usa como argumento de otra función «B». De esta forma, al llamar a «B», esta ejecutará «A». Esta acción puede ser inmediata, lo que se denominará retrollamada sincronizada o puede producirse en un punto posterior, lo que se denominaría retrollamada asíncrona. Los lenguajes de programación son compatibles con distintos tipos de retrollamadas, en ocasiones implementándolas con subrutinas, expresiones lambda o bloques de código.

Una retrollamada suele estar al mismo nivel del llamador original.

Esto permite desarrollar capas de abstracción de código genérico a bajo nivel que pueden ser llamadas desde una subrutina (o función) definida en una capa de mayor nivel. Por lo general, el código de alto nivel empieza con la llamada de alguna función, definida a bajo nivel, pasando a esta un puntero o un puntero inteligente de alguna otra función. Mientras la función de bajo nivel se ejecuta, esta puede ejecutar a su vez la función pasada como puntero para realizar alguna tarea. En otro escenario, las funciones de bajo nivel registran otras funciones pasadas como puntero inteligente, que luego pueden ser utilizadas para realizar retrollamadas asíncronas.

Se puede utilizar una retrollamada como aproximación simple al polimorfismo y a la programación genérica, donde el comportamiento de una función puede ser determinado dinámicamente por el paso punteros o punteros inteligentes a funciones de bajo nivel en las que, aunque realicen tareas diferentes, los argumentos sean compatibles entre sí. A esta técnica se le conoce como reutilización de código.

Diseño

En otro escenario común, la retrollamada se registra pero no se realiza hasta más tarde, lo que se denomina retrollamada asíncrona.

Estas funciones de retrollamadas tienen una gran variedad de usos. Por ejemplo, podría ser una función que lee un archivo de configuración y que asocia valores con opciones. Si las opciones están identificadas con un hash, entonces se escribe la función tal que haga una retrollamada, haciéndolo lo hace más flexible: su usuario puede elegir qué algoritmo de hash desea y la función continuará trabajando, ya que usa la retrollamada para cambiar los nombres de las opciones en los hashes; así, las retrollamadas permiten al usuario de una función personalizarla durante su ejecución.

Se puede considerar como ejemplo el problema de realizar varias operaciones arbitrarias en una lista. Una opción puede ser iterar sobre la lista, o también realizar alguna operación sobre cada uno de los elementos de la lista. En la práctica, la solución más común, pero no ideal, es utilizar iteradores (como un bucle for) que deberán duplicarse en cada lugar del código donde sea necesario. Más aún, si la lista es actualizada por un proceso asíncrono (por ejemplo, si un elemento es añadido o eliminado), en el iterador podría corromperse durante su paso a través de la lista.

Una alternativa podría ser crear una nueva biblioteca de funciones que ejecute la tarea deseada con la sincronización apropiada en cada caso. Esta propuesta aún requiere que cada nueva función de la biblioteca contenga el código para ir a través de la lista. Esta solución no es aceptable para bibliotecas genéricas que tengan como objetivo varias aplicaciones; el desarrollador de la biblioteca no puede anticiparse a las necesidades de cada aplicación y el desarrollador de las aplicaciones no debería necesitar conocer los detalles de la implementación de la biblioteca.

En este caso las retrollamadas resuelven estos problemas. Un procedimiento es escribir el paso a través de una lista que provee a la aplicación del código para ir a través de la lista, operando sobre cada elemento. Existe una clara distinción entre la biblioteca y la aplicación sin sacrificar la flexibilidad. Una retrollamada también se puede considerar un tipo de rutina enlazada por referencia.

Otro uso es en la señalización de errores. Un programa en un sistema operativo Unix, por ejemplo, podría no querer terminar inmediatamente cuando recibe un SIGTERM; para tomar los recaudos necesarios, una función de retrollamada podría efectuar una limpieza.

También puede utilizarse para controlar si una función actúa o no: Xlib permite predicados personalizables a especificarse para determinar si un programa desea manipular un evento.

Implementación

La forma de una retrollamada varía según el lenguaje de programación:

  • En lenguaje ensamblador, C, C++, Pascal, Modula-2 y otros lenguajes similares, se pasa un puntero como argumento para otra función (interna o externa). Esto es compatible con la mayoría de compiladores y proporciona la ventaja de usar distintos lenguajes a la vez sin tener que usar bibliotecas ni clases especiales. Un ejemplo podría ser la interfaz de programación de aplicaciones de Windows, que es más o menos accesible directamente desde varios lenguajes, compiladores y ensambladores.
  • C++ permite a los objetos proporcionar su propia implementación de la operación de llamada de la función. La Standard Template Library acepta estos objetos de función, así como punteros, como parámetros para diversos algoritmos polimórficos.
  • Algunos lenguajes de programación 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 de seguridad de tipos para definir los punteros de función. Estos se pueden usar como retrollamadas.
  • Los eventos y eventos inteligentes, propios de lenguajes .NET, proporcionan una sintaxis generalizada para retrollamadas.
  • Los lenguajes funcionales suelen ser compatibles con una función de primera clase, que puede pasar como retrollamadas para otras funciones, ser almacenada como datos o volver.
  • Algunos lenguajes, como Algol 68, Perl, Python, Ruby, Smalltalk, C++11 y, posteriormente, versiones recientes de C# y VB.NET, así como la mayoría de lenguajes funcoinales, permiten bloques de código sin nombre (expresión lambda) en lugar de referencias para definir funciones.
  • En algunos lenguajes informáticos, como Scheme, ML, JavaScript, Perl, Python, Smalltalk, PHP (desde 5.3.0),[4] C++11 en adelante, Java (desde 8),[5] y muchos otros, cuyas funciones pueden ser cerradas, por ejemplo, si pueden acceder o modificar variables localmente definidas en el contexto de la función. Java, sin embargo, no puede modificar las variables locales.
  • En lenguajes de programación orientada a objetos sin argumentos de valor de función, como en Java antes de la versión 8, las retrollamadas se pueden simular pasando una capa abstracta, en la que el receptor llamará a uno o más métodos, mientras que el final de llamada proporciona una implementación concreta. Dichos objetos son, en efecto, un lote de retrollamadas, además de los datos que necesitan para poder manipular. Son útiles para implementar varios patrones de diseño como patrón visitor, patrón observador y patrón estrategia.

Uso

C

El siguiente código en C demuestra el uso de funciones de retrollamada para mostrar dos números:

#include <stdio.h>
#include <stdlib.h>

/* La función de llamada toma una simple retrollamada como un parámetro. */
void ImprimirDosNumeros(int (*numeroEntrada)(void)) {
    printf("%d y %d\n", numeroEntrada(), numeroEntrada());
}

/* Una posible retrollamada. */
int unaRetrollamada(void) {
    return (rand() % 1000) + 9001;
}

/* Otra posible retrollamada. */
int otraRetrollamada(void) {
    return 42;
}

/* El programa principal llama a ImprimrDosNumeros() con tres retrollamadas diferentes. */
int main(void) {
    ImprimirDosNumeros(&rand);
    ImprimirDosNumeros(&unaRetrollamada);
    ImprimirDosNumeros(&otraRetrollamada);
    return 0;
}

Este ejemplo daría una salida en consola similar a:

  125185 y 89188225
  9084 y 9441
  42 y 42

JavaScript

Las retrollamadas se usan para la implementación de lenguajes como JavaScript, incluyendo el soporte de funciones JavaScript y retrollamadas mediante js-ctypes y en componentes como addEventListener.[6] Sin embargo, un ejemplo nativo de una retrollamada puede escribirse sin ningún código complejo:

function calculate(num1, num2, callbackFunction) {
    return callbackFunction(num1, num2);
}

function calcProduct(num1, num2) {
    return num1 * num2;
}

function calcSum(num1, num2) {
    return num1 + num2;
}
// alerta a 75, producto de 5 y 15
alert(calculate(5, 15, calcProduct));
// alerta a 20, la suma de 5 y 15
alert(calculate(5, 15, calcSum));

Lua

Ejemplo de colores con el motor de Roblox con una retrollamada .done opcional:

wait(1)
local DT = wait()

function tween_color(object, finish_color, fade_time)
  local step_r = finish_color.r - object.BackgroundColor3.r
  local step_g = finish_color.g - object.BackgroundColor3.g
  local step_b = finish_color.b - object.BackgroundColor3.b
  local total_steps = 1/(DT*(1/fade_time))
  local completed;
  coroutine.wrap(function()
    for i = 0, 1, DT*(1 / fade_time) do
      object.BackgroundColor3 = Color3.new (
        object.BackgroundColor3.r + (step_r/total_steps),
        object.BackgroundColor3.g + (step_g/total_steps),
        object.BackgroundColor3.b + (step_b/total_steps)
      )
      wait()
    end
    if completed then
      completed()
    end
  end)()
  return {
    done = function(callback)
      completed = callback
    end
  }
end

tween_color(some_object, Color3.new(1, 0, 0), 1).done(function()
  print "Color tweening finished!"
end)

Python

Un uso clásico de las retrollamadas en Python (y otros lenguajes de programación) es asignar eventos a elementos de la interfaz.

Aquí hay un pequeño ejemplo del uso de una retrollamada en Python. Primero define dos funciones, la retrollamada y el código de llamada. Después pasa la función de retrollamada al código de llamada.

>>> def get_square(val):
...     """La retrollamada."""
...     return val ** 2
...
>>> def caller(func, val):
...     return func(val)
...
>>> caller(get_square, 5)
25


Véase también

Referencias

  1. «What is a callback function?». Stack Overflow. Consultado el 16 de mayo de 2018.
  2. «Perl Cookbook - 11.4. Taking References to Functions». Consultado el 3 de marzo de 2008.
  3. «Advanced Perl Programming - 4.2 Using Subroutine References». Consultado el 3 de marzo de 2008.
  4. «Referencia del lenguaje PHP: Funciones anónimas». Consultado el 8 de junio de 2011.
  5. «Novedades en JDK 8». oracle.com.
  6. «Creating Javascript Callbacks in Components». Mozilla Developer Network. Archivado desde el original el 3 de noviembre de 2013. Consultado el 13 de diciembre de 2012.

Enlaces externos

Este artículo ha sido escrito por Wikipedia. El texto está disponible bajo la licencia Creative Commons - Atribución - CompartirIgual. Pueden aplicarse cláusulas adicionales a los archivos multimedia.