将参数传递给setInterval会导致奇怪的行为

时间:2012-04-10 09:08:00

标签: javascript node.js setinterval

我正在制作一个简单的脚本,读出node.js中的nginx服务器状态 我不认为我的问题与node.js本身有关,但更多的是我如何使用setInterval()函数。

我不想粘贴所有代码,因为这使得阅读有点混乱。 运行此代码时,您会看到:

pre-Timer for web25
pre-Timer for web26
pre-Timer for web27
pre-Timer for web28
pre-Timer for web29
Timer for web29
Fetch host? web29
Timer for web29
Fetch host? web29
Timer for web29
Fetch host? web29
Timer for web29
Fetch host? web29
Timer for web29
Fetch host? web29

如您所见,计时器仅使用循环的最后一个主机。不知何故,它没有将变量的副本复制到setInterval范围。

我做错了什么?

部分代码:

var http = require('http');

StatsNginxMapper.prototype = {

nginxServers: new Object(),
mysql: null,
mysqlClient: null,
statsDb: 'serverstats',
userMapper: null,

init: function() {
    this.mysql = require('mysql');
    this.mysqlClient = /* mysql stuff */;

    this.collectData();
},

setUserMapper: function(mapper) {
    this.userMapper = mapper;
},

collectData: function() {
    this.collectServers();
},

collectServers: function() {

    var self = this;
    var server = null;

    /* Normally this is done through MySQL, but for now lets do it manually */

    server = new StatsNginxServer();
    server.setHost('web25');
    this.nginxServers['web25'] = server;

    server = new StatsNginxServer();
    server.setHost('web26');
    this.nginxServers['web26'] = server;

    server = new StatsNginxServer();
    server.setHost('web27');
    this.nginxServers['web27'] = server;

    server = new StatsNginxServer();
    server.setHost('web28');
    this.nginxServers['web28'] = server;

    server = new StatsNginxServer();
    server.setHost('web29');
    this.nginxServers['web29'] = server;

    this.loopServers();

},

loopServers: function() {

    for(var host in this.nginxServers) {
        var nginxServer = this.nginxServers[host];

        if(nginxServer.hasTimer()) continue;

        var self = this;
        console.log('pre-Timer for ' + host);

        var timerId = setInterval(function() {

            console.log('Timer for ' + host);

            self.getData(host);

            }, 2000);

        nginxServer.setTimer(timerId);

    }

},

getData: function(host) {
    console.log('Fetch host? ' + host);
},

}

2 个答案:

答案 0 :(得分:1)

将loopServers函数更改为:

loopServers: function() {

    for(var host in this.nginxServers) {            
        var nginxServer = this.nginxServers[host];

        if(nginxServer.hasTimer()) continue;

        var self = this;
        console.log('pre-Timer for ' + host);
        (function(host, nginxServer, self) {
            var timerId = setInterval(function() {    
                console.log('Timer for ' + host);   
                self.getData(host);   
            }, 2000);

            nginxServer.setTimer(timerId);
        })(host, nginxServer, self);
    };
}

这将在setInterval代码周围创建一个闭包,该闭包将保留该点的变量值。

Information about closures

答案 1 :(得分:1)

问题是在一个间隔上运行的函数在调用时使用范围内的变量执行。这意味着,每个中间调用“可见”的主机是循环中的最后一个(在您的情况下是web29)。

解决方案是确保运行的函数始终在范围内具有正确的函数。一种方法是:

var timerId = setInterval(function(host) {
    return function() {
        console.log('Timer for ' + host);
        self.getData(host);
    }
}(host), 2000);

在这里,我们创建一个新函数,它返回您想要运行的函数。我们在创建时立即运行该函数,并在此时传递'host'的值。这样,执行日志记录的函数将始终具有创建时在范围内的主机。

我试图简化语言以使其更清晰,但我不确定我是否已经成功。如果你仔细阅读我写的内容,希望它能看得清楚。