Lección 20 · 20 de 20
Proyecto: cola de pedidos · JavaScript
Proyecto final en JavaScript: una cola de pedidos que combina clases con campos privados, eventos de EventEmitter y procesamiento con async/await.

Este proyecto reúne buena parte del curso en una cola de pedidos. OrderQueue es una clase que hereda de EventEmitter, guarda su estado en campos privados (#pending y #stock) y procesa los pedidos de forma asíncrona. El constructor copia el inventario con { ...stock }, de modo que nadie de fuera pueda cambiarlo.
add() mete el pedido en la cola y emite queued. La clase no escribe nada en la consola: solo avisa con eventos, y es el código de fuera el que decide qué hacer con cada aviso (aquí, escribir una línea; en una aplicación real, actualizar la pantalla o enviar un correo).
run() es un método async: saca los pedidos uno a uno con shift(), espera con await delay(...) para simular el empaquetado y comprueba las existencias. Si no hay bastantes, emite failed con el motivo y pasa al siguiente con continue; si las hay, las descuenta, suma el importe y emite packed. Al vaciarse la cola, emite idle con la facturación y el inventario final.
Los pedidos se crean a partir de un array de tuplas con desestructuración, y como run() devuelve una promesa, then escribe el mensaje final cuando todo ha terminado. Esta misma estructura (un servicio con estado, eventos y trabajo asíncrono) es la que encontrará en una tienda hecha con los destinos Electron, React o Next.js de DMarlon Studio.
Código · main.js
const EventEmitter = require("events");const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));// An order queue: private state, events and asynchronous work in one class.class OrderQueue extends EventEmitter { #pending = []; #stock; constructor(stock) { super(); this.#stock = { ...stock }; } add(order) { this.#pending.push(order); this.emit("queued", order); } async run() { let revenue = 0; while (this.#pending.length > 0) { const order = this.#pending.shift(); await delay(10 * order.qty); // pretend to pack the parcel const have = this.#stock[order.item] ?? 0; if (have < order.qty) { this.emit("failed", order, `only ${have} ${order.item} left`); continue; } this.#stock[order.item] = have - order.qty; revenue += order.qty * order.price; this.emit("packed", order); } this.emit("idle", revenue, this.#stock); }}const queue = new OrderQueue({ book: 5, lamp: 1 });queue.on("queued", (o) => console.log(`queued #${o.id}: ${o.qty} x ${o.item}`));queue.on("packed", (o) => console.log(`packed #${o.id}`));queue.on("failed", (o, reason) => console.log(`failed #${o.id}: ${reason}`));queue.on("idle", (total, stock) => console.log(`idle: ${total} EUR, stock ${JSON.stringify(stock)}`));[["book", 2, 15], ["lamp", 2, 40], ["book", 3, 15]].forEach(([item, qty, price], i) => queue.add({ id: i + 1, item, qty, price }));queue.run().then(() => console.log("All orders processed"));Salida · node main.js
queued #1: 2 x book
queued #2: 2 x lamp
queued #3: 3 x book
packed #1
failed #2: only 1 lamp left
packed #3
idle: 75 EUR, stock {"book":0,"lamp":1}
All orders processedPara recordar
- Los campos privados protegen la cola y el inventario.
- Los eventos separan la lógica de lo que se hace con cada resultado.
- Un método
asyncprocesa la cola en orden conawait. - Un pedido que falla no detiene el resto de la cola.
Ejercicio
Añada un método cancel(id) que quite un pedido pendiente y emita cancelled, y cambie run() para que un pedido sin existencias vuelva al final de la cola una sola vez antes de darse por fallido.