将parameter passing给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 

正如你所看到的,定时器只使用循环的最后一个主机。 不知何故,它并没有将variables的副本作为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); }, 

}

问题是运行在一个时间间隔上的函数会在调用时在范围内的variables执行。 这意味着,对每个插入呼叫“可见”的主机是循环中最后一个(在你的情况下,web29)。

解决scheme是确保运行的函数始终具有正确的范围。 这样做的一个方法是:

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

在这里,我们创build一个新的函数,返回你想要运行的函数。 而且我们在创build时立即运行这个函数,当时通过'host'的值。 这样,执行日志logging的function将始终具有创build时在范围内的主机。

我试图简化语言以使其更清晰,但我不确定我是否成功。 如果你阅读我写的东西,希望它应该看得很清楚。

将loopServersfunction更改为:

 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代码周围创build一个闭包,它将保留那个点的variables值。

关于closures的信息