Node.js+TypeScript中EventEmitter引用报错:Cannot read property 'EventEmitter' of undefined
我有一个运行在Node.js上的TypeScript应用,尝试用EventEmitter触发变量值变更,但执行时出现了报错。
我的代码:
import events from 'events'; public async updateStream(streamContext: string, state: boolean): Promise<string> { const eventEmitter = new events.EventEmitter(); if (state === true) { return StreamManagement.instance.activeStreams.get(streamContext).streamState = 'Paused'; } else { const streamState = StreamManagement.instance.activeStreams.get(streamContext).streamState = 'Active'; eventEmitter.emit('resume'); return streamState; } } public async waitForStreamActive(stream: Stream) { const eventEmitter = new events.EventEmitter(); // tslint:disable-next-line:no-unused-expression return new Promise(( resolve ) => { eventEmitter.on('resume', resolve ); }); }
报错信息:
error: errorHandler - Apply - Hit Unhandled exception {"timestamp":"2019-04-29T12:33:49.209Z"} error: errorHandler - Apply - Cannot read property 'EventEmitter' of undefined - TypeError: Cannot read property 'EventEmitter' of undefined at StreamResource.updateStream (C:\Vertigo\core\reference_platform\dist\index.js:10695:51) at StreamService.patchStream (C:\Vertigo\core\reference_platform\dist\index.js:22524:40) at process._tickCallback (internal/process/next_tick.js:68:7) {"timestamp":"2019-04-29T12:33:49.215Z"}
请问我哪里操作有误?
1. 先解决"Cannot read property 'EventEmitter' of undefined"报错
这个错误的核心是events模块的导入方式不对,导致events变量变成了undefined。
Node.js的events模块里,EventEmitter是作为命名导出存在的,不是默认导出的根对象。你用import events from 'events'的方式,在某些TypeScript编译配置下,没法正确获取到模块的导出内容,所以才会出现events为undefined的情况。
修复步骤:
修改导入语句,直接解构导入EventEmitter:
import { EventEmitter } from 'events';
之后创建实例时,直接用new EventEmitter()代替new events.EventEmitter():
// updateStream方法内 const eventEmitter = new EventEmitter(); // waitForStreamActive方法内 const eventEmitter = new EventEmitter();
2. 修复事件监听无效的逻辑问题
除了导入错误,你的代码还有个关键逻辑漏洞:每个方法都新建了独立的EventEmitter实例。
updateStream里的EventEmitter和waitForStreamActive里的是完全不同的对象,所以你在updateStream中触发的resume事件,waitForStreamActive里的监听器根本接收不到,Promise永远不会resolve。
修复步骤:
把EventEmitter实例作为类的属性共享,不要在每个方法里重复创建:
import { EventEmitter } from 'events'; class YourClassName { // 定义共享的EventEmitter实例 private streamEventEmitter = new EventEmitter(); public async updateStream(streamContext: string, state: boolean): Promise<string> { if (state === true) { StreamManagement.instance.activeStreams.get(streamContext).streamState = 'Paused'; return 'Paused'; } else { const streamState = 'Active'; StreamManagement.instance.activeStreams.get(streamContext).streamState = streamState; // 使用共享实例触发事件 this.streamEventEmitter.emit('resume'); return streamState; } } public async waitForStreamActive(stream: Stream) { return new Promise((resolve) => { // 使用共享实例监听事件 this.streamEventEmitter.on('resume', resolve); }); } }
另外,你原来代码里return StreamManagement.instance.activeStreams.get(streamContext).streamState = 'Paused';这种赋值后直接返回的写法可读性很差,建议改成先赋值再返回,就像上面修复后的代码那样。
额外优化建议
如果你的应用需要针对不同流触发独立事件,可以考虑给每个流创建对应的EventEmitter实例,或者在触发事件时携带流的标识,监听时过滤对应流的事件,避免不同流的事件互相干扰。
内容的提问来源于stack exchange,提问作者Shaik Syed Ali




