Node.js中HTTP服务器与普通EventEmitter事件触发行为差异解析
为什么HTTP服务器的listening事件能在注册监听器后触发执行?
差异的核心原因很简单——httpServer.listen()是异步操作,而普通EventEmitter.emit()是同步触发的。
普通EventEmitter的行为解释
在你给出的普通EventEmitter代码里:
const event = require("events"); const emitter = new event.EventEmitter(); emitter.emit("test"); // 同步触发事件 emitter.on("test", () => { console.log("test event"); });
emit("test")是同步执行的,这时候还没有任何监听器绑定到test事件上,事件触发后没有对应的处理逻辑,自然不会有输出。后续注册的监听器只会响应之后触发的同名事件,不会回溯处理已经触发过的事件。
HTTP服务器的行为解释
而HTTP服务器的listen()方法完全不同:
const http = require("http"); const httpServer = http.createServer(); httpServer.listen(3000); // 启动异步端口绑定流程 httpServer.on("listening", () => { console.log(`Event: listening`); });
调用listen(3000)时,Node.js并不会立即完成端口绑定并触发listening事件,它只是把「绑定端口」这个操作放到了事件循环的异步任务队列中。当前的同步代码块(也就是注册listening监听器的代码)会先执行完毕,等事件循环轮到处理这个异步任务时,才会真正完成端口绑定,然后触发listening事件——这时候监听器已经注册好了,所以会正常执行并打印内容。
额外验证
你可以做个小测试来验证这个逻辑:
- 在HTTP服务器代码里,把注册监听器的代码放到
setImmediate里(让它进入下一轮事件循环):
const http = require("http"); const httpServer = http.createServer(); httpServer.listen(3000); setImmediate(() => { httpServer.on("listening", () => { console.log(`Event: listening`); }); });
这时候大概率不会输出内容,因为端口绑定的异步任务可能已经执行完毕,listening事件已经触发过了。
- 在普通EventEmitter代码里,把
emit改成异步触发:
const event = require("events"); const emitter = new event.EventEmitter(); setTimeout(() => { emitter.emit("test"); }, 0); // 把emit放到异步队列 emitter.on("test", () => { console.log("test event"); });
这时候就会输出test event,因为emit是在监听器注册完成后才触发的。
内容的提问来源于stack exchange,提问作者Mohamed Kamal
相关产品推荐
相关产品推荐

