我知道这可能会标记为重复的解决方案,但堆栈溢出上的解决方案对我来说不起作用。
问题
(node:5716) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message lis teners added. Use emitter.setMaxListeners() to increase limit.
我的代码库非常庞大,有时我会遇到这个错误,但我不知道为什么会发生这种情况。我尝试增加监听器限制,但不幸的是,它不起作用。
const EventEmitter = require('events'); const emitter = new EventEmitter() emitter.setMaxListeners(50)
更新
经过一些浏览后,我运行此命令来跟踪警告
node --trace-warnings index.babel.js
事实证明,问题出在我的 socket.io 代码上,我在使用 Redis 时遇到了这个错误
node:14212) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message li steners added. Use emitter.setMaxListeners() to increase limit at _addListener (events.js:281:19) at RedisClient.addListener (events.js:298:10) at Namespace.<anonymous> (D:/newProject/services/socket.js:21:17) at emitOne (events.js:115:13) at Namespace.emit (events.js:210:7) at Namespace.emit (D:\newProject\node_modules\socket.io\lib\namespace.js:213:10) at D:\newProject\node_modules\socket.io\lib\namespace.js:181:14 at _combinedTickCallback (internal/process/next_tick.js:131:7) at process._tickCallback (internal/process/next_tick.js:180:9)
这是代码(但此代码用于更具体的任务,它不会一直执行)。
const redis = require('redis'); const config = require('../config'); const sub = redis.createClient(config.REDIS.port, config.REDIS.host); const pub = redis.createClient(config.REDIS.port, config.REDIS.host); sub.subscribe('spread'); module.exports = io => { io.on('connection', socket => { /* To find the User Login */ let passport = socket.handshake.session.passport; if (typeof passport !== 'undefined') { socket.on('typing:send', data => { pub.publish('spread', JSON.stringify(data)); }); sub.on('message', (ch, msg) => { // This is the Exact line where I am getting this error io.emit(`${JSON.parse(msg).commonID}:receive`, { ...JSON.parse(msg) }); }); } }); };
events中的内置模块node.js(如果您使用 或 进行编译,则该模块的一个版本会捆绑到您的前端应用程序中webpack)browserify会对您的代码做出一些假设。有时,在某个地方,有人认为如果您X注册了多个侦听器,那么您肯定存在内存泄漏。有时它是正确的,并正确地提醒您去查找泄漏。
events
node.js
webpack
browserify
X
我已多次收到此警告,但通常仅由于两个特定原因,并且这两个原因都有简单的解决方案:
您的组件可能看起来像这样,其中您使用组件方法作为事件监听器,并且在注册时绑定它。
import events from '../lib/events' // some singleton event emitter class MyComponent extends React.Component { componentDidMount() { events.addEventListener('some-event', this.myMethod.bind(this)) } componentWillUnmount() { events.removeEventListener('some-event', this.myMethod.bind(this)) } myMethod() { // does something } render() { // gotta have this too } }
这里的问题是每次function.bind都会创建一个新函数,因此您尝试删除的函数与您添加的函数不同。因此,添加的函数不断增加(糟糕的双关语),您确实存在真正的内存泄漏。
function.bind
尽早绑定方法,通常在 中constructor()。然后每次都可以引用绑定的版本,确保删除的函数与添加的函数相同。
constructor()
import events from '../lib/events' // some singleton event emitter class MyComponent extends React.Component { constructor() { // bind your method early so the function removed // is the same as the function added this.myMethod = this.myMethod.bind(this) } componentDidMount() { events.addEventListener('some-event', this.myMethod) } componentWillUnmount() { events.removeEventListener('some-event', this.myMethod) } myMethod() { // does something } render() { // gotta have this too } }
有时,你确实做了功课,并仔细检查了是否已根据需要尽早绑定了侦听器,然后在适当的位置将它们全部删除。然后你仔细查看,发现你正在做这样的事情:
import MyComponent from './MyComponent' // same component above class Parent extends React.Component { render() { return ( <div> { this.props.largeArray.map(MyComponent) } </div> ) } }
假设this.props.largeArray有 50、100 或 250 个元素。这意味着(根据设计!)您将渲染 250 个实例MyComponent,每个实例都注册另一个唯一的事件侦听器。
this.props.largeArray
MyComponent
别担心!这是完全有效的代码,没有内存泄漏。但它确实突破了最大监听器限制,这是某个人在某个时间某个地方任意决定的,以保护您。
eventemitter3
如果您决定已经完成作业,并仔细检查了所有内容,并且(按照设计!)注册了大量的事件监听器,那么最简单的解决方案是切换到使用eventemitter3,它是节点模块的替代品events,但速度更快,与浏览器兼容,并且不为您设置最大监听器限制。
使用方法与内置events模块一样:
const EventEmitter = require('eventemitter3') const emitter = new EventEmitter()