如何在Node.js的EventEmitter中正确地移除事件监听器

42

我有一个包含GET和POST路由的设置,其想法是POST到路由会触发一个事件,而GET路由是一个服务器发送事件流,每次触发POST事件时都会触发... 但是,我认为我做错了什么,因为即使只有一个事件流订阅者,事件监听器似乎会被定期添加... 我做错了什么?

var events = require('events'),
EventEmitter = events.EventEmitter,
rr = new EventEmitter();

app.post('/api/:boardname/remoterefresh', function(req, res){
    var boardname = req.param('boardname'),
    data = new Date().getTime();
    rr.emit("refresh-"+boardname, data)
    res.json({data: data})
});

app.get('/api/:boardname/remoterefresh', function(req, res){
    var boardname = req.param('boardname')

    rr.on("refresh-"+boardname, function(data){
        setTimeout(function(){
            res.write('data: '+data+'\n\n');
        }, 1000)
    });

    req.socket.setTimeout(Infinity);

    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    res.write('\n');

    req.on('close', function(){
        console.log('closed')
        rr.removeListener("refresh-"+boardname, function(){
            //
        })
    })

})

1
可能是Node.js中解除事件绑定的重复问题 - Farid Nouri Neshat
这会导致竞态条件并跨请求泄漏数据吗? - Charlie Schliesser
2个回答

59

你应该给所附加的事件处理函数命名。然后在删除它时,只需通过名称传递该函数:

app.get('/api/:boardname/remoterefresh', function(req, res){
    var boardname = req.param('boardname')
    function refreshHandler(data){
        setTimeout(function(){
            res.write('data: '+data+'\n\n');
        }, 1000)
    }
    rr.on("refresh-"+boardname, refreshHandler);

    req.socket.setTimeout(Infinity);

    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    res.write('\n');

    req.on('close', function(){
        console.log('closed')
        rr.removeListener("refresh-"+boardname, refreshHandler);
    });
});

基本上removeListener会通过引用查找给定的函数,如果找到该函数,它将从事件处理程序中移除它。


谢谢你,你是救命恩人 :) 我开玩笑的,但我找到了解决我的问题的方法。 - SefaUn

7
为了避免引用匿名的监听器回调函数,你可以使用 emitter.removeAllListeners 方法来危险地清除已注册的监听器:
rr.removeAllListeners("refresh-"+boardname)

请注意,这种方法很容易导致意想不到的副作用(取消已在代码库其他位置注册的侦听器),应该保留给“彻底清除所有内容”的情况(例如:测试)。


2
请注意,rr.removeAllListeners(复数形式)是正确的名称。 - Gershom Maes
我还发现当removeListener无法识别与function.bind(this)绑定的类中的方法名称时,我需要使用removeAllListeners,因此也许那种方式不起作用。 - scipilot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接