You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Node.js+TypeScript中EventEmitter引用报错:Cannot read property 'EventEmitter' of undefined

问题: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

火山引擎 最新活动