¿Cuándo debo usar la programación basada en eventos?


65

He estado pasando devoluciones de llamada o simplemente activando las funciones de otra función en mis programas para hacer que las cosas sucedan una vez que se completen las tareas. Cuando algo termina, disparo la función directamente:

var ground = 'clean';

function shovelSnow(){
    console.log("Cleaning Snow");
    ground = 'clean';
}

function makeItSnow(){
    console.log("It's snowing");
    ground = 'snowy';
    shovelSnow();
}

Pero he leído sobre muchas estrategias diferentes en programación, y una que entiendo que es poderosa, pero que aún no he practicado, está basada en eventos (creo que un método sobre el que leí se llamaba "pub-sub" ):

var ground = 'clean';

function shovelSnow(){
    console.log("Cleaning Snow");
    ground = 'clean';
}

function makeItSnow(){
    console.log("It's snowing");
    ground = 'snowy';
    $(document).trigger('snow');
}

$(document).bind('snow', shovelSnow);

Me gustaría comprender las fortalezas y debilidades objetivas de la programación basada en eventos, en lugar de simplemente llamar a todas sus funciones desde otras funciones. ¿En qué situaciones de programación tiene sentido utilizar la programación basada en eventos?


2
Como comentario, solo puedes usar $(document).bind('snow', shovelShow). No es necesario envolverlo en una función anónima.
Karl Bielefeldt

44
También podría estar interesado en aprender sobre la "programación reactiva", que tiene mucho en común con la programación basada en eventos.
Eric Lippert el

Respuestas:


75

Un evento es una notificación que describe una ocurrencia del pasado reciente.

Una implementación típica de un sistema controlado por eventos utiliza un despachador de eventos y funciones de manejo (o suscriptores ). El despachador proporciona una API para conectar controladores a eventos (jQuery's bind) y un método para publicar un evento a sus suscriptores ( triggeren jQuery). Cuando habla de eventos IO o UI, también suele haber un bucle de eventos , que detecta nuevos eventos, como los clics del mouse, y los pasa al despachador. En JS-land, el navegador proporciona el despachador y el bucle de eventos.

Para el código que interactúa directamente con el usuario, respondiendo a las pulsaciones de teclas y clics, la programación controlada por eventos (o una variación de la misma, como la programación reactiva funcional ) es casi inevitable. Usted, el programador, no tiene idea de cuándo o dónde hará clic el usuario, por lo que depende del marco de la GUI o del navegador detectar la acción del usuario en su bucle de eventos y notificar su código. Este tipo de infraestructura también se usa en aplicaciones de red (cf NodeJS).

Su ejemplo, en el que genera un evento en su código en lugar de llamar a una función directamente, tiene algunas compensaciones más interesantes, que analizaré a continuación. La principal diferencia es que el editor de un evento ( makeItSnow) no especifica el receptor de la llamada; eso está conectado a otra parte (en la llamada a binden su ejemplo). Esto se llama disparar y olvidar : makeItSnowanuncia al mundo que está nevando, pero no le importa quién está escuchando, qué sucede después o cuándo sucede; simplemente transmite el mensaje y se quita el polvo de sus manos.


Por lo tanto, el enfoque basado en eventos desacopla al remitente del mensaje del receptor. Una ventaja que esto le brinda es que un evento determinado puede tener múltiples controladores. Puede vincular una gritRoadsfunción a su evento de nieve sin afectar el shovelSnowcontrolador existente . Tiene flexibilidad en la forma en que su aplicación está conectada; para desactivar un comportamiento, solo necesita eliminar la bindllamada en lugar de buscar el código para encontrar todas las instancias del comportamiento.

Otra ventaja de la programación basada en eventos es que le brinda un lugar para plantear inquietudes transversales. El despachador de eventos desempeña el papel de Mediador , y algunas bibliotecas (como Brighter ) utilizan una canalización para que pueda conectar fácilmente los requisitos genéricos, como el registro o la calidad de servicio.

Divulgación completa: Brighter se desarrolla en Huddle, donde trabajo.

Una tercera ventaja de desacoplar el remitente de un evento del receptor es que le brinda flexibilidad cuando maneja el evento. Puede procesar cada tipo de evento en su propio hilo (si su despachador de eventos lo admite), o puede colocar eventos generados en un intermediario de mensajes como RabbitMQ y manejarlos con un proceso asincrónico o incluso procesarlos en masa durante la noche. El receptor del evento podría estar en un proceso separado o en una máquina separada. ¡No tiene que cambiar el código que genera el evento para hacer esto! Esta es la gran idea detrás de las arquitecturas de "microservicios": los servicios autónomos se comunican mediante eventos, con el middleware de mensajería como la columna vertebral de la aplicación.

Para un ejemplo bastante diferente de estilo basado en eventos, mire el diseño controlado por dominio, donde los eventos de dominio se utilizan para ayudar a mantener los agregados separados. Por ejemplo, considere una tienda en línea que recomienda productos basados ​​en su historial de compras. A Customerdebe tener actualizado su historial de compras cuando ShoppingCartse paga. El ShoppingCartagregado puede notificar al Customermediante un CheckoutCompletedevento; la Customerobtendría actualizada en una transacción separada en respuesta al evento.


El principal inconveniente de este modelo basado en eventos es la indirección. Ahora es más difícil encontrar el código que maneja el evento porque no puede navegar hasta él usando su IDE; debe averiguar dónde está vinculado el evento en la configuración y esperar que haya encontrado todos los controladores. Hay más cosas para tener en mente en cualquier momento. Las convenciones de estilo de código pueden ayudar aquí (por ejemplo, colocar todas las llamadas binden un archivo). Por el bien de su cordura, es importante usar solo un despachador de eventos y usarlo de manera consistente.

Otra desventaja es que es difícil refactorizar eventos. Si necesita cambiar el formato de un evento, también debe cambiar todos los receptores. Esto se exacerba cuando los suscriptores de un evento están en máquinas diferentes, porque ahora necesita sincronizar las versiones de software.

En ciertas circunstancias, el rendimiento puede ser una preocupación. Al procesar un mensaje, el despachador debe:

  1. Busque los controladores correctos en alguna estructura de datos.
  2. Cree una canalización de procesamiento de mensajes para cada controlador. Esto puede involucrar un montón de asignaciones de memoria.
  3. Llamar dinámicamente a los manejadores (posiblemente usando la reflexión si el lenguaje lo requiere).

Esto es ciertamente más lento que una llamada de función normal, lo que solo implica empujar un nuevo marco en la pila. Sin embargo, la flexibilidad que le brinda una arquitectura basada en eventos hace que sea mucho más fácil aislar y optimizar el código lento. Tener la capacidad de enviar trabajo a un procesador asíncrono es una gran victoria aquí, ya que le permite atender una solicitud de inmediato mientras el trabajo duro se trata en segundo plano. En cualquier caso, si está interactuando con el DB o dibujando cosas en la pantalla, los costos de IO reducirán totalmente los costos de procesamiento de un mensaje. Se trata de evitar la optimización prematura.


En resumen, los eventos son una excelente manera de construir software acoplado libremente, pero no están exentos de costos. Sería un error, por ejemplo, reemplazar cada llamada de función en su aplicación con un evento. Use eventos para hacer divisiones arquitectónicas significativas.


2
Esta respuesta dice lo mismo que la respuesta de 5377 que seleccioné como correcta; Estoy cambiando mi selección para marcar esta porque se desarrolla más.
Viziionario

1
¿Es la velocidad una desventaja significativa del código controlado por eventos? Parece que podría ser, pero no lo sé.
raptortech97

1
@ raptortech97 ciertamente puede ser. Para el código que necesita ser particularmente rápido, probablemente desee evitar enviar eventos en un bucle interno; Afortunadamente, en tales situaciones, generalmente está bien definido lo que debe hacer, por lo que no necesita la flexibilidad adicional de los eventos (o publicación / suscripción u observadores, que son mecanismos equivalentes con una terminología diferente).
Jules

1
También tenga en cuenta que hay algunos idiomas (por ejemplo, Erlang) construidos alrededor del modelo de actor donde todo es mensajes (eventos). En este caso, el compilador puede decidir si implementa los mensajes / eventos como llamadas a funciones directas o como comunicación.
Brendan

1
Para el "rendimiento", creo que debemos distinguir entre el rendimiento de un solo subproceso y la escalabilidad. Los mensajes / eventos pueden ser peores para el rendimiento de un solo subproceso (pero se pueden convertir en llamadas a funciones por cero costo adicional y no ser peores), y para la escalabilidad es superior en casi todos los sentidos (por ejemplo, es probable que resulte en mejoras masivas de rendimiento en multi moderno -CPU y futuros sistemas "muchas CPU").
Brendan

25

La programación basada en eventos se usa cuando el programa no controla la secuencia de eventos que realiza. En cambio, el flujo del programa está dirigido por un proceso externo como un usuario (por ejemplo, GUI), otro sistema (por ejemplo, cliente / servidor) u otro proceso (por ejemplo, RPC).

Por ejemplo, un script de procesamiento por lotes sabe lo que debe hacer, por lo que simplemente lo hace. Es no basado en eventos.

Un procesador de texto se sienta allí y espera a que el usuario comience a escribir. Las pulsaciones de teclas son eventos que activan la funcionalidad para actualizar el búfer interno del documento. El programa no puede saber lo que desea escribir, por lo que debe estar controlado por eventos.

La mayoría de los programas GUI están controlados por eventos porque se basan en la interacción del usuario. Sin embargo, los programas basados ​​en eventos no se limitan a las GUI, ese es simplemente el ejemplo más familiar para la mayoría de las personas. Los servidores web esperan que los clientes se conecten y siguen un lenguaje similar. Los procesos en segundo plano en su computadora también pueden responder a eventos. Por ejemplo, un escáner de virus a pedido puede recibir un evento del sistema operativo con respecto a un archivo recién creado o actualizado, luego escanear ese archivo en busca de virus.


18

En una aplicación basada en eventos, el concepto de oyentes de eventos le dará la capacidad de escribir aún más aplicaciones acopladas libremente .

Por ejemplo, un módulo o complemento de un tercero puede eliminar un registro de la base de datos y luego activar el receordDeletedevento y dejar el resto a los oyentes del evento para que hagan su trabajo. Todo funcionará bien, aunque el módulo de activación ni siquiera sabe quién está escuchando este evento en particular o qué debería suceder a continuación.


6

Una analogía simple que quería agregar que me ayudó:

Piense en los componentes (u objetos) de su aplicación como un gran grupo de amigos de Facebook.

Cuando uno de tus amigos quiere decirte algo, puede llamarte directamente o publicarlo en su muro de Facebook. Cuando lo publican en su Facebook, cualquiera puede verlo y reaccionar, pero mucha gente no lo ve. A veces es algo importante que las personas probablemente necesiten reaccionar, como "¡Vamos a tener un bebé!" o "¡La banda está haciendo un concierto sorpresa en el bar Drunkin 'Clam!". En el último caso, entonces el resto de los amigos probablemente tendrán que reaccionar, especialmente si están interesados ​​en esa banda.

Si su amigo quiere mantener un secreto entre usted y ellos, probablemente no lo publicarán en su muro de Facebook, lo llamarán directamente y se lo dirán. Imagine un escenario en el que le dice a una chica que le gusta que le gustaría encontrarse con ella en un restaurante para una cita. En lugar de llamarla directamente y preguntarle, lo publica en su muro de Facebook para que lo vean todos sus amigos. Esto funciona, pero si tienes un ex celoso, entonces ella podría ver eso y aparecer en el restaurante para arruinarte el día.

Cuando decida si desea construir o no oyentes de eventos para implementar algo, piense en esta analogía. ¿Este componente necesita poner su negocio a la vista de todos? ¿O necesitan llamar a alguien directamente? Las cosas pueden complicarse con bastante facilidad, así que ten cuidado.


0

La siguiente analogía podría ayudarlo a comprender la programación de E / S impulsada por eventos dibujando un paralelo a la línea de espera en el mostrador de Recepción del Doctor.

Bloquear E / S es como, si estás parado en la cola, la recepcionista le pide a un chico frente a ti que complete el formulario y ella espera hasta que él termine. Tienes que esperar tu turno hasta que el chico termine su forma, esto está bloqueando.

Si el hombre soltero tarda 3 minutos en completarse, el décimo hombre tiene que esperar hasta 30 minutos. Ahora, para reducir este décimo tiempo de espera, la solución sería aumentar el número de recepcionistas, lo cual es costoso. Esto es lo que sucede en los servidores web tradicionales. Si solicita una información de usuario, la solicitud posterior de otros usuarios debe esperar hasta que se complete la operación actual, ir a buscar desde la base de datos. Esto aumenta el "tiempo de respuesta" de la décima solicitud y aumenta exponencialmente para el enésimo usuario. Para evitar esto, los servidores web tradicionales crean un subproceso (equivalente a un número creciente de recepcionistas) para cada solicitud individual, es decir, básicamente, crea una copia del servidor para cada solicitud, lo cual es costoso en términos de consumo de CPU, ya que cada solicitud necesitará un sistema operativo hilo. Para ampliar la aplicación,

Impulsado por eventos : El otro enfoque para ampliar el "tiempo de respuesta" de la cola es optar por un enfoque impulsado por eventos, donde los que están en la cola recibirán el formulario, se les pedirá que completen y regresen al finalizar. Por lo tanto, recepcionista siempre puede tomar la solicitud. Esto es exactamente lo que ha estado haciendo JavaScript desde su inicio. En el navegador, javascript respondería al evento de clic del usuario, desplazamiento, desplazamiento o búsqueda de la base de datos, etc. Esto es posible en JavaScript inherentemente, porque JavaScript trata las funciones como objetos de primera clase y se pueden pasar como parámetros a otras funciones (llamadas devoluciones de llamada), y se pueden invocar al completar una tarea en particular. Esto es exactamente lo que hace node.js en el servidor. Puede encontrar más información sobre programación dirigida por eventos y bloqueo de E / S, en el contexto del nodo aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.