Search code examples
javascriptnode.jsnode-red

How to manage multiple listeners?


I have a problem with listeners. I have multiple input within a http listener in my own developed node. I used somethig like this:

...    
server.on(‘connection’, function(){
        node.on(‘input’, function(){
            // something to manage messages
        });
    });
...

I would like to choose when stop a listener (for example, when it finds a specific msg.payload). I tried “node.once(.. “, “node.removeListener(..” but nothing. Can you help me? Thanks.

EDIT

All code:

var server = http.createServer((request, response) => {
    if (request.url == '/log' || request.url == '/log2'){
        let body = [];
        request.on('error', (err) => {
            console.error(err);
        }).on('data', (chunk) => {
            body.push(chunk);
        }).on('end', () => {
            body = Buffer.concat(body).toString();

            response.on('error', (err) => {
                console.error(err);
            });

            response.statusCode = 200;
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Content-Type", "text/html");

            msg = {_msgid:"1c6db704.811319", topic:request.url ,payload: true};
            node.send(msg);


            function inputListener(msg) {
                console.log(msg.topic + " " + request.url);
                if (msg.topic == request.url)
                    response.end(JSON.stringify(msg.payload));

                //node.removeListener('input', inputListener);
            }
            node.on('input', inputListener);

        });
    } else {
        response.statusCode = 404;
        response.end();
    }
}).listen(8088);

EDIT 2

It seems to work using this trick:

            function inputListener(msg) {
                if (msg.topic == request.url)
                    response.end(JSON.stringify(msg.payload));
                else
                    node.once('input', inputListener);
            }
            node.once('input', inputListener);

Solution

  • node.removeListener() will work just fine if you use it properly so I'm assuming you aren't using it properly. To use .removeListener() properly to remove one specific listener, you have to pass it the message you originally listened to and the same function reference. That means you can't use an anonymous function reference like you show:

    Instead, you can do something like this:

    server.on('connection', function(){
        function inputListener(data) {
            // something to manage messages
            if (someCondition) {
                // if some condition is met, then remove this specific listener
                node.removeListener('input', inputListener);
            }
        }
        node.on('input', inputListener);
    });
    

    Here the code creates a local function (which will be unique every time the connection handler is called) and then uses that with .on(). That, then allows you to pass the exact same function reference to .removeListener() when you want to remove that specific listener. It may be worth reviewing the doc for .removeListener().

    node.once() has it's own specific purpose too. What it does is automatically remove the event listener after the next time the event fires. If that's exactly the situation you want, it is very convenient. But, if you want to test some condition and only remove the event handler if that condition is met and sometimes need to see multiple occurrences of the event, then you can't use .once() for that.

    P.S. Your question contains an illegal type of quote mark around for your event names. That will not work in Javascript. You should never be editing code in a word processor that does that type of processing. Always use a text editor.


    Now that you've included more of your real code, it appears you have a concurrency issue because you're using the same node object for all requests and thus you can't tell which input event belongs to which request. You will need to solve that issue.

    There are many ways to do it, but here's one way. You will need to change the node object so it receives a unique ID that you send and returns it back with the response so you can tell which response goes with which request.

    const unique = require('node-unique');
    
    var server = http.createServer((request, response) => {
        if (request.url == '/log' || request.url == '/log2'){
            let body = [];
            request.on('error', (err) => {
                console.error(err);
            }).on('data', (chunk) => {
                body.push(chunk);
            }).on('end', () => {
                body = Buffer.concat(body).toString();
    
                response.on('error', (err) => {
                    console.error(err);
                });
    
                response.statusCode = 200;
                response.setHeader("Access-Control-Allow-Origin", "*");
                response.setHeader("Content-Type", "text/html");
    
                // generate unique id for this request
                let uniqueID = unique();
                msg = {_msgid:"1c6db704.811319", topic:request.url ,payload: true, id: uniqueID};
                node.send(msg);
    
                function inputListener(msg) {
                    // ignore if this is not our msg
                    if (msg.id === uniqueID) {
                        console.log(msg.topic + " " + request.url);
                        if (msg.topic == request.url)
                            response.end(JSON.stringify(msg.payload));
    
                        node.removeListener('input', inputListener);
                    }
                }
                node.on('input', inputListener);
            });
        } else {
            response.statusCode = 404;
            response.end();
        }
    }).listen(8088);