En software , un bucle de eventos es un algoritmo que gestiona continuamente el flujo de control en función de los eventos . El bucle solicita el siguiente evento a un proveedor de eventos (que generalmente bloquea el bucle hasta que se produce un evento) y, al recibirlo, invoca su controlador de eventos asociado . Cuando el bucle de eventos es el gestor central de eventos de un programa , como suele ser el caso, se denomina bucle principal o bucle de eventos principal .
En entornos modernos como navegadores web y servidores, el bucle de eventos es un mecanismo fundamental que permite la ejecución asíncrona mediante la monitorización continua y el envío de eventos o mensajes desde una cola cuando el hilo principal del programa está inactivo. En JavaScript, el bucle de eventos permite el manejo no bloqueante de tareas como interacciones de usuario, temporizadores y operaciones de E/S, a pesar de que el lenguaje sea de un solo hilo. [ 1 ]
El mismo algoritmo puede utilizarse para procesar mensajes entrantes , un superconjunto de eventos. En este contexto, el algoritmo se denomina bucle de mensajes , distribuidor de mensajes o bomba de mensajes . Un uso común de un bucle de mensajes es la comunicación entre procesos mediante el paso de mensajes, donde la cola de mensajes se mantiene fuera del programa (por ejemplo, mediante el sistema operativo ).
Normalmente, un programa que funciona en un entorno de interfaz gráfica de usuario (GUI) utiliza un bucle de eventos, y debido al predominio de los entornos GUI, la mayoría de las aplicaciones modernas tienen un bucle de eventos principal.
En el siguiente pseudocódigo, get_next_message()es un marcador de posición para una función que normalmente proporciona el sistema operativo y que se bloquea hasta que haya un mensaje disponible. Por lo tanto, el bucle solo se repite cuando hay un mensaje para procesar.
bucle mensaje := obtener_siguiente_mensaje() procesar_mensaje(mensaje) mientras mensaje != salir
Alternativas
Muchos programas incluyen un bucle de eventos en el diseño de alto nivel; un bucle de eventos principal, pero existen diseños alternativos.
Un programa puede finalizar tan pronto como complete las acciones para las que fue diseñado, sin interacción externa (es decir, del usuario ). A menudo, este tipo de programa incluye comportamientos opcionales que se configuran antes de su inicio, por ejemplo, mediante la interfaz de línea de comandos (CLI) o variables de entorno . El software de utilidad suele ser de esta naturaleza.
Aunque un programa permita la interacción del usuario, es posible que no utilice un bucle de eventos. Un programa que proporciona una interfaz de línea de comandos interna incluye un procesador de comandos similar a un bucle de eventos, ya que envía comandos a los manejadores en función de la entrada del usuario.
Ejemplos
HTML/JavaScript
Una página web y su código JavaScript suelen ejecutarse en un proceso de navegador web de un solo hilo . Este proceso procesa los mensajes de una cola uno a uno. Cada mensaje puede estar asociado a una función JavaScript u otro evento del navegador. Cuando el navegador termina de procesar un mensaje, pasa al siguiente de la cola.
En entornos JavaScript, el bucle de eventos trabaja con colas separadas —incluida una cola de tareas y una cola de microtareas— para gestionar operaciones asíncronas como temporizadores, devoluciones de llamada de promesas y eventos DOM. Cuando la pila de llamadas está vacía, el bucle de eventos selecciona la siguiente tarea de estas colas para ejecutarla. [ 2 ]
Aplicaciones de Windows
En Windows , un proceso que interactúa con el usuario debe aceptar y responder a los mensajes entrantes, lo cual se realiza casi inevitablemente mediante un bucle de mensajes dentro de dicho proceso. En Windows, un mensaje se equipara a un evento creado e impuesto al sistema operativo. Un evento puede ser la interacción del usuario, el tráfico de red, el procesamiento del sistema, la actividad del temporizador, la comunicación entre procesos, entre otros. Para eventos no interactivos, de solo E/S, Windows cuenta con puertos de finalización de E/S . Los bucles de los puertos de finalización de E/S se ejecutan de forma independiente del bucle de mensajes y, por defecto, no interactúan con él.
El núcleo de la mayoría de las aplicaciones Win32 es la función WinMain() , que llama a GetMessage() en un bucle. GetMessage() se bloquea hasta que se recibe un mensaje (evento) (con la función PeekMessage() como alternativa no bloqueante). Tras un procesamiento opcional, llama a DispatchMessage() , que distribuye el mensaje al controlador correspondiente, también conocido como WindowProc . Normalmente, los mensajes que no tienen un WindowProc() específico se distribuyen a DefWindowProc , el predeterminado. DispatchMessage() llama al WindowProc del identificador HWND del mensaje (registrado con la función RegisterClass() ).
Las versiones más recientes de Windows garantizan que los mensajes se entregarán al bucle de mensajes de una aplicación en el orden en que fueron percibidos por el sistema y sus periféricos. Esta garantía es esencial al considerar las consecuencias del diseño de aplicaciones multihilo . Sin embargo, algunos mensajes tienen reglas diferentes, como los que siempre se reciben al final o los que tienen una prioridad documentada distinta. [ 3 ]
Bucle de eventos de Xlib
Las aplicaciones X que utilizan Xlib directamente se construyen en torno a la XNextEventfamilia de funciones; XNextEventse bloquean hasta que aparece un evento en la cola de eventos, momento en el que la aplicación lo procesa adecuadamente. El bucle de eventos de Xlib solo maneja eventos del sistema de ventanas; las aplicaciones que necesitan poder esperar en otros archivos y dispositivos podrían construir su propio bucle de eventos a partir de primitivas como ConnectionNumber, pero en la práctica tienden a usar multihilo .
Muy pocos programas utilizan Xlib directamente. En el caso más común, los kits de herramientas GUI basados en Xlib suelen admitir la adición de eventos. Por ejemplo, los kits de herramientas basados en Xt Intrinsics tienen XtAppAddInput()y XtAppAddTimeout().
No es seguro llamar a funciones de Xlib desde un manejador de señales, porque la aplicación X puede haber sido interrumpida en un estado arbitrario, por ejemplo, dentro de XNextEvent. Verpara una solución para X11R5, X11R6 y Xt.
Bucle de eventos de GLib
El bucle de eventos de GLib se creó originalmente para su uso en GTK , pero ahora también se utiliza en aplicaciones sin interfaz gráfica, como D-Bus . El recurso consultado es la colección de descriptores de archivo que interesan a la aplicación; el bloque de consulta se interrumpirá si llega una señal o si expira un tiempo de espera (por ejemplo, si la aplicación ha especificado un tiempo de espera o una tarea inactiva). Si bien GLib cuenta con soporte integrado para eventos de terminación de descriptores de archivo y procesos secundarios, es posible agregar una fuente de eventos para cualquier evento que pueda gestionarse mediante un modelo de preparación, verificación y despacho.
Las bibliotecas de aplicaciones basadas en el bucle de eventos de GLib incluyen GStreamer y los métodos de E/S asíncronos de GnomeVFS , pero GTK sigue siendo la biblioteca cliente más visible. Los eventos del sistema de ventanas (en X , leídos desde el socket X ) son traducidos por GDK a eventos GTK y emitidos como señales de GLib en los objetos widget de la aplicación.
Bucles de ejecución de macOS Core Foundation
Se permite exactamente un bucle CFRunLoop por hilo, y se pueden conectar tantas fuentes y observadores como se desee. Las fuentes se comunican con los observadores a través del bucle de ejecución, que se encarga de organizar la cola y el envío de mensajes.
En Cocoa, el CFRunLoop se abstrae como un NSRunLoop, lo que permite que cualquier mensaje (equivalente a una llamada a una función en entornos de ejecución no reflexivos ) se ponga en cola para su envío a cualquier objeto.
Basado en archivos
En Unix , el paradigma de que todo es un archivo da lugar a un bucle de eventos basado en archivos. La lectura y escritura de archivos, la comunicación entre procesos, la comunicación de red y el control de dispositivos se realizan mediante E/S de archivos, donde el destino se identifica mediante un descriptor de archivo . Las llamadas al sistema `select` y `poll` permiten monitorizar un conjunto de descriptores de archivo para detectar cambios de estado, por ejemplo, cuando hay datos disponibles para su lectura.
Por ejemplo, consideremos un programa que lee un archivo que se actualiza continuamente y muestra su contenido en el Sistema X Window , que se comunica con los clientes a través de un socket (ya sea de dominio Unix o Berkeley ):
def main (): file_fd = open ( "logfile.log" ) x_fd = open_display () construct_interface () while True : rlist , _ , _ = select.select ([ file_fd , x_fd ], [ ] , []): if file_fd in rlist : data = file_fd.read ( ) append_to_display ( data ) send_repaint_message () if x_fd in rlist : process_x_messages ( )Basado en señales
En Unix, una señal es un evento asíncrono que es gestionado por un manejador de señales que se ejecuta mientras el resto de la tarea está suspendida. Si se recibe y se gestiona una señal mientras la tarea está bloqueada select(), la instrucción select devolverá EINTR ; si se recibe una señal mientras la tarea está limitada por la CPU , la tarea se suspenderá entre instrucciones hasta que el manejador de señales devuelva un valor.
Una forma de gestionar una señal es que los manejadores de señales establezcan un indicador global y que el bucle de eventos compruebe dicho indicador inmediatamente antes y después de la select()llamada; si está establecido, se gestiona la señal del mismo modo que con los eventos en los descriptores de archivo. Desafortunadamente, esto genera una condición de carrera : si una señal llega inmediatamente entre la comprobación del indicador y la llamada select(), no se gestionará hasta select()que la función devuelva un resultado por algún otro motivo (por ejemplo, si un usuario frustrado la interrumpe).
La solución a la que llegó POSIX es la pselect()llamada, que es similar a select()pero toma un sigmaskparámetro adicional, que describe una máscara de señal . Esto permite que una aplicación enmascare señales en la tarea principal, luego elimine la máscara durante la duración de la select()llamada de modo que los manejadores de señales solo se llamen mientras la aplicación está limitada por E/S . Sin embargo, las implementaciones de pselect()no siempre han sido confiables; las versiones de Linux anteriores a 2.6.16 no tienen una pselect()llamada al sistema, [ 4 ] forzar a glibc a emularla a través de un método propenso a la misma condición de carrera pselect()se pretende evitar.
Una solución alternativa y más portátil consiste en convertir eventos asíncronos en eventos basados en archivos utilizando el truco de la tubería propia , [ 5 ] donde "un manejador de señales escribe un byte en una tubería cuyo otro extremo es monitoreado select()en el programa principal". [ 6 ] En la versión 2.6.22 del kernel de Linux , se agregó una nueva llamada al sistema signalfd()que permite recibir señales a través de un descriptor de archivo especial.
JavaScript y ejecución asíncrona
En entornos como navegadores web y Node.js, el bucle de eventos es fundamental para el comportamiento asíncrono de los programas. JavaScript se ejecuta en un único hilo, pero los entornos anfitriones proporcionan API para operaciones no bloqueantes, como temporizadores, solicitudes de red y eventos de usuario. Las operaciones asíncronas completadas se colocan en colas, y el bucle de eventos comprueba repetidamente si la pila de llamadas está vacía para programar las devoluciones de llamada en cola. Esto permite que las aplicaciones JavaScript sigan respondiendo mientras gestionan múltiples tareas asíncronas. [ 7 ]
Véase también
Referencias
- ↑ "Modelo de ejecución de JavaScript - JavaScript | MDN" . MDN Web Docs . 2025-11-07 . Consultado el 2026-01-03 .
- ↑ "Modelo de ejecución de JavaScript - JavaScript | MDN" . MDN Web Docs . 2025-11-07 . Consultado el 2026-01-03 .
- ↑ Función GetMessage() con lista de prioridad de mensajes.
- ↑ "Linux_2_6_16 - Linux Kernel Newbies" . kernelnewbies.org . Consultado el 3 de marzo de 2021 .
- ↑ DJ Bernstein. "El truco del tubo autoinducido" .
- ↑ ERRORES : multiplexación de E/S síncrona – Manual del programador de Linux – Llamadas al sistema
- ↑ "Modelo de ejecución de JavaScript - JavaScript | MDN" . MDN Web Docs . 2025-11-07 . Consultado el 2026-01-03 .
Enlaces externos
- Recorriendo el laberinto del enrutamiento de mensajes y comandos de MFC
- Uso de mensajes y colas de mensajes (MSDN)
- Uso de procedimientos de ventana (MSDN)
- WindowProc (MSDN)
- Flujo de control
- Eventos (informática)