1

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

}

4

2 に答える 2

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 コードの周りにクロージャーが作成されます。

休館情報

于 2012-04-10T09:19:43.863 に答える
1

問題は、間隔を置いて実行される関数が、呼び出し時にスコープ内にある変数で実行されることです。つまり、間隔を置いた呼び出しごとに「見える」ホストは、ループの最後のホスト (あなたの場合は web29) です。

解決策は、実行される関数が常に適切な関数をスコープ内に持つようにすることです。これを行う 1 つの方法は次のとおりです。

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

ここでは、実行したい関数を返す新しい関数を作成します。そして、作成直後にその関数を実行し、その時点で「host」の値を渡します。このように、ログを作成する関数には、作成時にスコープ内にあったホストが常に含まれます。

わかりやすくするために言葉を単純化しようとしましたが、成功したかどうかはわかりません。私が書いたことを読んでいただければ、それがはっきりとわかるはずです。

于 2012-04-10T09:28:52.227 に答える