He estado pasando devoluciones de llamada o simplemente activando las funciones de otras funciones en mis programas para hacer que las cosas sucedan una vez que se completan las tareas. Cuando algo termina, activo 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 yo «ve leí 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 del evento -basada en programación, vs simplemente llamar a todas sus funciones desde dentro de otras funciones. ¿En qué situaciones de programación tiene sentido utilizar la programación basada en eventos?
Comentarios
Respuesta
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 hasta eventos (jQuery «s bind
) y un método para publicar un evento para sus suscriptores (trigger
en jQuery). Cuando se habla de eventos IO o UI, también suele haber un bucle de eventos , que detecta nuevos eventos como clics del mouse y los pasa al despachador. JS-land, el despachador y el bucle de eventos son proporcionados por el navegador.
Para el código que interactúa directamente con el usuario, respondiendo a pulsaciones de teclas y clics, programación basada en eventos (o una variación de la misma, como programación reactiva funcional ) es casi inevitable. Usted, el programador, no tiene idea de cuándo o dónde va a hacer clic el usuario, por lo que todo depende del marco de la GUI o navegador para detectar la acción del usuario en su bucle de eventos y notificar su código. Este tipo de infraestructura también se utiliza en aplicaciones de red (cf NodeJS).
Su ejemplo, en el que genera un evento en yo Nuestro código en lugar de llamar a una función directamente tiene algunas compensaciones más interesantes, que discutiré a continuación. La principal diferencia es que el editor de un evento (makeItSnow
) no especifica el receptor de la llamada; que «está conectado en otro lugar (en la llamada a bind
en su ejemplo). Esto se llama disparar y olvidar : makeItSnow
anuncia al mundo que está nevando, pero no le importa quién está escuchando, qué sucede a continuación o cuándo sucede; simplemente transmite el mensaje y se quita el polvo de las manos.
De modo que el enfoque controlado por eventos desacopla al remitente del mensaje del receptor. Una ventaja que esto le brinda es que un evento determinado puede tener varios controladores. Puede vincular una función gritRoads
a su evento de nieve sin afectar al controlador shovelSnow
existente. Tiene flexibilidad en la forma en que se conecta su aplicación; para desactivar un comportamiento, solo necesita eliminar la llamada bind
en lugar de buscar en el código para encontrar todas las instancias del comportamiento.
Otra ventaja de La programación impulsada por eventos es que le brinda un lugar para poner preocupaciones transversales. El despachador de eventos desempeña la función de Mediador , y algunas bibliotecas (como Brighter ) utilizan una canalización para que pueda incorporar fácilmente requisitos genéricos como el registro o la calidad del servicio.
Divulgación completa: Brighter se desarrolla en Huddle, donde trabajo.
Una tercera ventaja de desvincular el remitente de un evento del receptor es que le da flexibilidad en 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 agente de mensajes como RabbitMQ y manejarlos con un proceso asincrónico o incluso procesarlos a granel durante la noche. El receptor del evento podría estar en un proceso separado o en una máquina separada. ¡No es necesario cambiar el código que genera el evento para hacer esto! Esta es la gran idea detrás de las arquitecturas de «microservicio»: los servicios autónomos se comunican mediante eventos, con el middleware de mensajería como la columna vertebral de la aplicación.
Para obtener un ejemplo bastante diferente de estilo basado en eventos, busque el diseño basado en dominios, donde eventos de dominio se utilizan para ayudar a mantener los agregados separados. Por ejemplo, considere una tienda en línea que recomienda productos en función de su historial de compras. Un Customer
necesita actualizar su historial de compras cuando se paga un ShoppingCart
. El agregado ShoppingCart
podría notificar al Customer
generando un evento CheckoutCompleted
; Customer
se actualizaría en una transacción separada en respuesta al evento.
La principal desventaja 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; tienes que averiguar dónde está vinculado el evento en la configuración y esperar que hayas encontrado todos los controladores. Hay más cosas para tener en la cabeza en cualquier momento. Las convenciones de estilo de código pueden ayudar aquí (por ejemplo, poner todas las llamadas a bind
en 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 los eventos. Si necesita cambiar el formato de un evento, también debe cambiar todos los receptores. Esto se agrava cuando los suscriptores de un evento están en diferentes máquinas, ¡porque ahora necesita sincronizar las versiones de software!
En ciertas circunstancias, el rendimiento puede ser una preocupación. Al procesar un mensaje, el despachador tiene que:
- Buscar los manejadores correctos en alguna estructura de datos.
- Construir una canalización de procesamiento de mensajes para cada manejador. Esto puede involucrar un montón de asignaciones de memoria.
- Llame dinámicamente a los controladores (posiblemente usando la reflexión si el lenguaje lo requiere).
Esto es ciertamente más lento que una función regular call, que solo implica empujar un nuevo marco en la pila. Sin embargo, la flexibilidad que le ofrece 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 ventaja aquí, ya que le permite atender una solicitud de inmediato mientras el trabajo duro se resuelve en segundo plano. En cualquier caso, si está interactuando con la base de datos o dibujando cosas en la pantalla, los costos de IO hundirán totalmente los costos de procesar un mensaje. Es un caso de evitar la optimización prematura.
En resumen, los eventos son una excelente manera de crear software de acoplamiento flexible, pero no están exentos de costos. Sería un error, por ejemplo, reemplazar cada llamada a función en su aplicación con un evento. Utilice eventos para crear divisiones arquitectónicas significativas.
Comentarios
- Esta respuesta dice lo mismo que 5377 ‘ s respuesta que seleccioné como correcta; Yo ‘ estoy cambiando mi selección para marcar esta porque es más detallada.
- ¿Es la velocidad una desventaja significativa del código controlado por eventos? Parece que podría ser, pero ‘ no lo sé.
- @ raptortech97 ciertamente puede ser. Para el código que necesita ser particularmente rápido, probablemente querrá evitar enviar eventos en un bucle interno; afortunadamente, en tales situaciones, por lo general, está bien definido lo que debe hacer, por lo que no ‘ no necesita la flexibilidad adicional de los eventos (o publicar / suscribirse u observadores, que son mecanismos equivalentes con terminología diferente).
- También tenga en cuenta que hay algunos lenguajes (por ejemplo, Erlang) construidos alrededor del modelo de actor donde todo son mensajes (eventos). En este caso, el compilador puede decidir si implementar los mensajes / eventos como llamadas de función directas o como comunicación.
- Para » 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 de función sin costo adicional y no ser peor), y para la escalabilidad es ‘ superior en prácticamente todos manera (por ejemplo, que probablemente resulte en mejoras masivas de rendimiento en sistemas modernos de múltiples CPU y futuros » muchas CPU «).
Respuesta
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 es 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 necesita hacer, así que simplemente lo hace. no se basa 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 de documentos interno. 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 a 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 bajo demanda 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.
Responder
En una aplicación basada en eventos, el concepto de Event Listeners le dará la capacidad de escribir aún más Aplicaciones sueltas .
Por ejemplo, un módulo o complemento de terceros puede eliminar un registro de la base de datos y luego activar el receordDeleted
event y deje 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.
Responder
Una analogía simple que quería agregar y 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 contarte algo, puede llamarte directamente o publicarlo en su muro de Facebook. Cuando lo publican en Facebook, cualquiera podría verlo y reaccionar ante él, pero mucha gente no. A veces es algo importante que la gente probablemente tendrá que reaccionar, como «¡Vamos a tener un bebé!» o «Fulano de tal banda está dando un concierto sorpresa en el Drunkin «¡Barra de almejas!». En el último caso, es probable que el resto de los amigos deban reaccionar, especialmente si están interesados en esa banda.
Si su amigo quiere mantener un secreto entre usted y ellos, probablemente no lo publicarían en su muro de Facebook, te llamarían directamente y te lo dirían. Imagina un escenario en el que le dices a una chica que te gusta que te gustaría conocerla en un restaurante para una cita. En lugar de llamarla directamente y preguntar ella, lo publicas en tu muro de Facebook para que todos tus amigos lo vean. Esto funciona, pero si tienes un ex celoso, entonces ella podría ver eso y aparecer en el restaurante para arruinar tu día.
Cuando Para decidir si incorporar o no oyentes de eventos para implementar algo, piense en esta analogía. ¿Es necesario que este componente exponga su negocio para que todos lo vean? ¿O necesitan llamar a alguien directamente? Las cosas pueden complicarse con bastante facilidad, así que cuidado.
Respuesta
Esta siguiente analogía podría ayudarte u comprender la programación de E / S impulsada por eventos trazando una línea paralela a la de espera en el mostrador de recepción del médico.
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 termine. Tienes que esperar tu turno hasta que el chico termine su forma, esto es bloquear.
Si un chico solo tarda 3 minutos en completar, el décimo 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 información de usuario, la solicitud posterior de otros usuarios debe esperar hasta el Se completa la operación actual, obtenida de 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 subprocesos (equivalente a un número creciente de recepcionistas) para cada solicitud , 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 subproceso de sistema operativo. Para escalar la aplicación, tendría que aportar una gran cantidad de poder de cálculo a la aplicación .
impulsado por eventos: el otro enfoque para aumentar el tiempo de respuesta de la cola es optar por un enfoque impulsado por eventos, donde los chicos en la cola recibirán el formulario, se le pide que lo rellene y vuelva al cumplimentarlo. Por lo tanto, la recepcionista siempre puede aceptar solicitudes. Esto es exactamente lo que javascript ha estado haciendo desde sus inicios. En el navegador, javascript respondería al evento de clic del usuario, desplazamiento, deslizamiento o búsqueda de base de datos, etc. Esto es posible en javascript inherentemente, porque javascript trata las funciones como de primera clase objetos y se pueden pasar como parámetros a otras funciones (llamadas devoluciones de llamada), y se pueden llamar al completar una tarea en particular.Esto es exactamente lo que hace node.js en el servidor.Puede encontrar más información sobre la programación impulsada por eventos y el bloqueo de E / S, en el contexto del nodo aquí
$(document).bind('snow', shovelShow)
. No es necesario incluirlo en una función anónima.