MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message lis teners added. Use emitter.setMaxListeners() to increase limit

前端 未结 4 1634
礼貌的吻别
礼貌的吻别 2020-12-20 13:33

I know this might flag as a duplicate solution but solution on stack overflow is not working for me.

Problem:

(node:5716) MaxListenersExceededWarning         


        
相关标签:
4条回答
  • 2020-12-20 14:15

    This is the recommended way to add and remove event listeners within React Components - using LifeCycle methods.

    import { Component } from 'react';
    
    class Example extends Component {
      constructor(props) {
       super(props);
    
       this.state = {
        windowWidth: window.innderWidth,
       };
      }
    
      componentDidMount() {
        window.addEventListener('resize', this.handleResize);
      }
    
      componentWillUnmount() {
        window.removeEventListener('resize', this.handleResize);
      }
    
      handleResize = () => {
        this.setState({ windowWidth: window.innerWidth });
      }
    
      render() {
        return (
          <div>
            Current window width: {this.state.windowWidth}
          </div>
        );
      }
    }
    

    It's important to remember that window is within the global execution context. Therefore every time you add an event listener, you're asking the global scope to

    1. instantiate another listener.
    2. track that listener using global memory by reference - in this case resize
    3. continue tracking the listener until told not to.

    If you never tell the global scope to remove those listeners, then the global memory - as allocated by your browser settings - will slowly evaporate and crash your browser & application, or the client's browser if already in production. One must be VERY careful and VERY aware when they manipulate global memory.

    If you want to understand (if you don't already) WHY the lifecycle methods in React Component's work, I would highly recommend you look here at React's Reconciliation lifecycle. One can't accurately call themselves a "React Developer" and not be intimately familiar with Reconciliation.

    Note This Component is using babel to transpile parts of the code: import, and assigned custom method handleResize with only using arrow functions. If you need assistance in setting up the environment, you can refer to this blog post i wrote that should make it understandable.

    Good luck.

    0 讨论(0)
  • 2020-12-20 14:19

    Default limit for Event Emitter is 10. You can increase it with emitter.setMaxListeners. My suggestion is not to change it unless and until explicitly required, listeners are increased because you didn't unsubscribed. Now to your code.

    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) => {
        //COMMENT : This callback will be executed for all the socket connections. 
            let passport  = socket.handshake.session.passport;  /* To find the User Login  */
            if(typeof passport !== "undefined") {
    
    
                socket.on('typing:send',(data) => {
    
                    pub.publish('spread',JSON.stringify(data))
                });
                // COMMENT : This is where you are subscribing for each and every socket conected to your server
                sub.on('message',(ch,msg) => { // this is the Exact line where I am getting this error
    
    
    //COMMENT : Where as you are emiting message on socket manager not on socket. 
    io.emit(`${JSON.parse(msg).commonID}:receive`,{...JSON.parse(msg)})
                })
    
    
            }
        });
    };

    Now if we analyse above code then if you open 20 socket connection to your server it will subscribe for 20 time, here it is going wrong. Now if your requirement is to listen for the message published on redis at server level and then emit on io then your code should be like below

    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 => {
    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)});
            });
        io.on('connection',(socket) => {
    
            let passport  = socket.handshake.session.passport;  /* To find the User Login  */
            if(typeof passport !== "undefined") {
    
    
                socket.on('typing:send',(data) => {
    
                    pub.publish('spread',JSON.stringify(data))
                });
                
    
    
            }
        });
    };

    0 讨论(0)
  • 2020-12-20 14:20

    The built-in events module in node.js (a version of which is bundled into your frontend app if you compile with webpack or browserify) makes some assumptions about your code. Sometime, somewhere, somebody decided that if you had X number of listeners registered, then surely you've got a memory leak. And sometimes it is correct, and reminds you correctly to go find the leaks.

    I've received this warning many times, but usually only for two specific reasons, both of which have simple solutions:


    Problem 1: Mismatched bound event listener functions

    Your component may look like this, where you are using a component method as the event listener, and you are binding it as you register it.

    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
      }
    }
    

    The problem here is that function.bind creates a new function each time, such that the function you are trying to remove is not the same as the function you added. Consequently, the added functions keep adding-up (bad pun), and you do in fact have a real memory leak.

    Solution 1: Bind your methods early

    Bind your method early, commonly in the constructor(). Then you can refer to the bound version each time, ensuring that the function removed is the same as the function added.

    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
      }
    }
    

    Problem 2: Lots and lots of event listeners

    Sometimes you've really done your homework and double-checked that you've bound your listeners early as needed, and then removed them all at the appropriate places. Then you look closer and find that you're doing something like this:

    import MyComponent from './MyComponent' // same component above
    
    class Parent extends React.Component {
      render() {
        return (
          <div>
            { this.props.largeArray.map(MyComponent) }
          </div>
        )
      }
    }
    

    Suppose this.props.largeArray had 50, 100, or maybe 250 elements. This means that (by design!) you are rendering 250 instances of MyComponent, each of which is registering another unique event listener.

    Fear not! This is totally valid code, and does not have a memory leak. But it does blow through the max-listeners limit that somebody, sometime, somewhere decided arbitrarily to help protect you.

    Solution 2: Switch to using eventemitter3

    If you decide that you've done your homework, and double-checked all the things, and are (by design!) registering lots of event listeners, then the simplest solution is to switch to using eventemitter3, which is a drop-in replacement for node's events module, except faster, and browser-compatible, and doesn't set a max-listeners limit for you.

    Usage is just like the built-in events module:

    const EventEmitter = require('eventemitter3')
    const emitter = new EventEmitter()
    
    0 讨论(0)
  • 2020-12-20 14:26

    MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message lis teners added. Use emitter.setMaxListeners() to increase limit

    By default, a maximum of 10 listeners can be registered for any single event, and we got 11 Ohno

    // Change to 80 or 150 whatever and see what happens
    require('events').EventEmitter.prototype._maxListeners = 70;
    require('events').defaultMaxListeners = 70;
    
      process.on('warning', function (err) {
        if ( 'MaxListenersExceededWarning' == err.name ) {
          console.log('o kurwa');
          // write to log function
          process.exit(1); // its up to you what then in my case script was hang
    
        }
      });
    
    0 讨论(0)
提交回复
热议问题