2012-04-10 2 views
1

Je fais un script simple lit l'état du serveur nginx dans node.js Je ne pense pas que mon problème a à voir avec node.js lui-même, mais plus un problème avec comment j'utilise la fonction setInterval().Passant des arguments à setInterval provoque un comportement étrange

Je ne veux pas coller tout le code, car cela rend la lecture un peu confuse. Lorsque vous exécutez ce code, vous voyez ceci:

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 

Comme vous pouvez le voir, la minuterie n'utilise le dernier hôte de la boucle. D'une manière ou d'une autre, il ne fait pas de copie de la variable dans la portée setInterval.

Qu'est-ce que je fais mal?

Une partie du code:

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); 
}, 

}

Répondre

1

Le problème est que la fonction qui fonctionne sur un intervalle, exécute avec les variables qui sont dans la portée quand il appelle. Qu'est-ce que cela signifie, c'est que l'hôte qui est «visible» à chaque appel par intervalle, est le dernier dans la boucle (dans votre cas, web29).

La solution est de s'assurer que la fonction qui s'exécutera aura toujours la bonne portée. Une façon de le faire est:

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

Ici, nous créons une nouvelle fonction, qui renvoie la fonction que vous souhaitez exécuter. Et nous exécutons cette fonction immédiatement après la création, en passant par la valeur de «hôte» à ce moment-là. De cette façon, la fonction qui effectue la journalisation aura toujours l'hôte qui était dans la portée au moment de la création.

J'ai essayé de simplifier le langage pour le rendre plus clair, mais je ne suis pas sûr d'avoir réussi. Si vous lisez ce que j'ai écrit, j'espère que cela devrait être clair.

+0

Merci David, j'aime aussi ta solution. Je vais voir lequel est le plus efficace dans mon cas –

1

Changer la fonction loopServers à ceci:

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

Cela va créer une fermeture autour du code setInterval qui préservera la les valeurs des variables à ce point.

Information about closures

+0

Merci! Works :) La partie amusante est, je jouais avec la fonction anonyme et avec ça ça a bien fonctionné, mais je n'ai pas essayé les deux! –

Questions connexes