DMarlon Studio

Lección 19 · 19 de 20

Eventos con EventEmitter · JavaScript

Comunique partes de un programa en Node con EventEmitter: emit, on, once, varios oyentes y el evento especial error.

JavaScript · Eventos con EventEmitter en DMarlon Studio: el código del ejemplo y su salida en la terminal

EventEmitter, del módulo events de Node, implementa el patrón publicar/suscribir: un objeto anuncia que algo ha pasado y quien esté interesado reacciona, sin que uno conozca al otro. Shop hereda de EventEmitter y, en buy(), emite order cuando hay venta o soldout cuando no quedan existencias.

`on(evento, función)` registra un oyente. Un mismo evento puede tener varios: uno anota el pedido y otro avisa al almacén cuando left llega a cero. Los argumentos que se pasan a `emit` (aquí, un objeto; en soldout, el artículo y el cliente) llegan tal cual a cada oyente.

Los oyentes se ejecutan de forma síncrona y en el orden en que se registraron, dentro de la propia llamada a emit. `once` registra uno que se ejecuta solo la primera vez y después se elimina, por eso first order of the day! sale una vez y listenerCount("order") vale 2 al final.

El evento `error` es especial: si se emite y nadie lo escucha, Node lanza el error, y si no se captura, el programa termina. Por eso conviene registrar siempre un oyente de error. Este patrón está en todo Node (servidores, flujos, procesos) y también en Electron, uno de los destinos de DMarlon Studio, donde la ventana y el proceso principal se comunican con eventos.

Código · main.js

const EventEmitter = require("events");// A class that extends EventEmitter can announce what happens inside it.class Shop extends EventEmitter {  constructor(stock) {    super();    this.stock = stock;  }  buy(customer, item) {    if (!this.stock[item]) return this.emit("soldout", item, customer);    this.stock[item] -= 1;    this.emit("order", { customer, item, left: this.stock[item] });  }}const shop = new Shop({ mug: 2 });shop.on("order", (o) => console.log(`Order: ${o.customer} bought a ${o.item}`));shop.on("order", (o) => o.left === 0 && console.log(`  warehouse: restock ${o.item}`));shop.once("order", () => console.log("  first order of the day!"));shop.on("soldout", (item, who) => console.log(`Sorry ${who}, the ${item} is sold out`));shop.buy("Ana", "mug");shop.buy("Luis", "mug");shop.buy("Marta", "mug");console.log("Listeners for order:", shop.listenerCount("order"));// "error" is special: emitting it with no listener throws.try {  shop.emit("error", new Error("payment gateway down"));} catch (err) {  console.log("Nobody listened:", err.message);}shop.on("error", (err) => console.log("Logged:", err.message));shop.emit("error", new Error("payment gateway down"));

Salida · node main.js

Order: Ana bought a mug
  first order of the day!
Order: Luis bought a mug
  warehouse: restock mug
Sorry Marta, the mug is sold out
Listeners for order: 2
Nobody listened: payment gateway down
Logged: payment gateway down

Para recordar

  • emit anuncia un evento con sus datos; on lo escucha.
  • Los oyentes se ejecutan de forma síncrona, en orden de registro.
  • once escucha solo la primera vez.
  • Un evento error sin oyente lanza una excepción.

Ejercicio

Añada un evento lowstock que se emita cuando queden menos de 2 unidades de un artículo, y use off con una función con nombre para quitar el oyente del almacén después de la primera reposición.