如何用Node.js EventEmitter实现顾客订阅取消事件及顾客列表管理
面包店EventEmitter场景完整实现方案
你的代码目前存在几个核心问题:事件回调无法被取消引用、顾客移除逻辑缺失、购买逻辑未用到预设的最高价和烤好的面包数量。以下是针对性的修正方案:
核心修改说明
- 可追踪的事件回调:匿名回调无法通过
off方法取消订阅,因此需要将回调函数绑定到顾客实例上,方便后续移除。 - 完整的顾客移除逻辑:既要从面包店的顾客列表中删除,也要同步取消对应的事件订阅。
- 完善的购买逻辑:加入顾客对面包价格的接受上限(
maxPrice)判断,同时结合面包库存处理购买行为。
修改后的bakery.js
const eventEmitter = require('./event-emitter.js'); const events = require('./events.js'); class Bakery { _breads; _customers; _breadPrice; constructor(breadPrice) { this._breads = 0; this._customers = []; this._breadPrice = breadPrice; } get breadPrice() { return this._breadPrice; } get breads() { return this._breads; } bakeBreads(breadsQuantity) { this._breads += breadsQuantity; console.log(`面包店烤好了 ${breadsQuantity} 个面包,当前库存:${this._breads}`); eventEmitter.emit(events.BREAD_BAKED, breadsQuantity); } addCustomer(customer) { // 避免重复添加同一顾客 if (this._customers.includes(customer)) return; this._customers.push(customer); // 将回调函数绑定到顾客实例,用于后续取消订阅 customer.buyCallback = () => { customer.buyBread(); }; // 订阅烤面包完成事件 eventEmitter.on(events.BREAD_BAKED, customer.buyCallback); customer.bakery = this; console.log(`顾客 ${customer._customerName} 已加入面包店`); } removeCustomer(customer) { // 从顾客列表中移除目标顾客 this._customers = this._customers.filter(c => c !== customer); // 取消对应的事件订阅 if (customer.buyCallback) { eventEmitter.off(events.BREAD_BAKED, customer.buyCallback); delete customer.buyCallback; } customer.bakery = null; console.log(`顾客 ${customer._customerName} 已离开面包店`); } } module.exports = Bakery;
修改后的customer.js
class Customer { _customerName; _maxPrice; _moneyAmount; _bakery; constructor(customerName, maxPrice, moneyAmount, bakery = null) { this._customerName = customerName; this._maxPrice = maxPrice; this._moneyAmount = moneyAmount; this._bakery = bakery; } set bakery(bakery) { this._bakery = bakery; } buyBread() { if (!this._bakery) return; // 三重判断:价格符合预期、有足够资金、面包店有库存 if (this._bakery.breadPrice > this._maxPrice) { console.log(`${this._customerName} 觉得面包太贵,放弃购买`); return; } if (this._moneyAmount < this._bakery.breadPrice) { console.log(`${this._customerName} 资金不足,买不起面包`); return; } if (this._bakery.breads <= 0) { console.log(`${this._customerName} 想买面包,但店里已售罄`); return; } // 执行购买操作 this._moneyAmount -= this._bakery.breadPrice; this._bakery._breads -= 1; // 购买一个面包,库存减1 console.log(`✅ ${this._customerName} 花费 ${this._bakery.breadPrice} 元购买面包,剩余余额:${this._moneyAmount.toFixed(2)},面包店剩余库存:${this._bakery.breads}`); } } module.exports = Customer;
修改后的main.js(测试用例)
const Bakery = require('./bakery.js'); const Customer = require('./customer.js'); // 假设你的event-emitter.js是Node原生events的实例封装: // module.exports = new(require('events').EventEmitter)(); // events.js定义事件常量:module.exports = { BREAD_BAKED: 'bread_baked' }; const bakery = new Bakery(2.5); const johnRich = new Customer('John', 3, 10); // John能接受最高3元,持有10元 const martinPoor = new Customer('Martin', 3, 1); // Martin能接受最高3元,仅持有1元 // 添加顾客 bakery.addCustomer(johnRich); bakery.addCustomer(martinPoor); // 第一次烤面包 console.log('\n--- 第一次烤面包 ---'); bakery.bakeBreads(2); // Martin离开面包店 console.log('\n--- Martin离开面包店 ---'); bakery.removeCustomer(martinPoor); // 第二次烤面包 console.log('\n--- 第二次烤面包 ---'); bakery.bakeBreads(3);
关键细节说明
- 事件订阅与取消:通过将回调函数挂载到
customer.buyCallback,移除时可直接用该引用调用eventEmitter.off,解决了匿名函数无法取消订阅的问题。 - 购买逻辑优化:补充了价格预期、库存判断,让场景更贴近真实业务逻辑。
- 重复添加防护:
addCustomer中加入重复判断,避免同一顾客被多次订阅事件。
内容的提问来源于stack exchange,提问作者dvgga
相关产品推荐
相关产品推荐

