0

私はPHPのバックグラウンドから来て、「イベント駆動型」のNode.js環境に頭を悩ませようとしています。ディレクトリからファイルを読み取り、Redis を番組のタイトル、シーズン、エピソード番号 (現在データベースに保存されているものより新しい場合) で更新する小さなスクリプトを作成しました。Redis DB のタイトルが「My Show」、シーズン「05」、タイトル「01」のどこに頭を悩ませることができない非同期の問題が発生しているようです。「My Show S05E02」と「My Show S05E01」を含む 2 つのファイルが読み込まれています。

データベースは、シーズン/エピソードが現在のシーズン/エピソードよりも後の場合にのみ更新する必要がありますが、「updateTitle」が非常に迅速に呼び出され、何らかの理由で「My Show S05E02」が「My Show S05E01」の前に渡されるため、更新関数は常に更新されます。両方の値を元の値「My Show S05E01」と比較し、Redis を E02 で更新し、次に E01 で更新します。

コードは次のとおりです。

function processFiles()
{
fs.readdir(WATCH_DIR, function(err, files){
    for (var i = 0; i <= files.length; i++)
    {
        checkFile(files[i]);
    }
});
}

function updateTitle(title, season, episode)
{
var cur_season, cur_episode;

redis_client.hget(title, 'Season', function(err, data){
    cur_season = data;
    redis_client.hget(title, 'Episode', function(err, data){
        cur_episode = data;
        redis_client.sismember('Titles', title, function(err, data){
            console.log('comparing S'+season+'E'+episode+' to current S'+cur_season+'E'+cur_episode);
            if ((season == cur_season && episode >= cur_episode) || season > cur_season)
            {
                redis_client.hset(title, 'Season', season);
                redis_client.hset(title, 'Episode', episode);
                console.log('setting '+title+' S'+season+'E'+episode);
            }
        });
    });
});
}

function checkFile(file, mtime)
{
var reg         = new RegExp("^"+FILE_PREFIX);
var seasoned    = new RegExp("S(\\d{2})E(\\d{2})", "i");

var cache = {}
if (reg.test(file))
{
    fs.stat(WATCH_DIR + file, function(err, stats){
        console.log(file, stats.mtime.toLocaleDateString() +' '+ stats.mtime.toLocaleTimeString() );
        fs.readFile(WATCH_DIR + file, 'utf8', function(ferr, data){
            if (seasoned.test(data))
            {
                title = data.replace(/S(\d{2})E(\d{2})(.*?)$/, '')
                    .replace(/[\._\-]+/, ' ')
                    .replace(/^\s+/, '')
                    .replace(/\s+$/, '');

                var season = data.match(/S(\d{2})/i);
                season = season[1];
                var episode = data.match(/E(\d{2})/i);
                episode = episode[1];
                updateTitle(title, season, episode);
            }
        });
    });
}
}

fs.watch(WATCH_DIR, function(type, file){
if (type == 'change')
{
    processFiles();
}
});

どんな助けでも大歓迎です。ここには他にもエラーやベストプラクティスがあると確信しています。それらも自由に共有してください。しかし、私は非同期の問題を理解しようとして頭を壁にぶつけています!

参考までに - これは単なるお気に入りのプロジェクトなので、私が見て楽しんでいる各番組の現在のエピソードを思い出すことができます。

4

3 に答える 3

2

問題は、ここでの実行順序が保証されていないことです。たとえば、次の順序で発生する可能性があります。

  1. ファイル 1 の読み取り
  2. Redis に最初の get リクエストを行う
  3. ファイル 2 の読み取り
  4. Redis に 2 番目の get リクエストを行う
  5. 最初に Redis から取得します。
  6. Redis からの 2 番目の get が返されます。
  7. 最初の設定を Redis に実行します。
  8. Redis に 2 番目のセットを実行します。

最初の操作が 2 番目の操作の結果に依存する場合、2 番目の操作を開始する前に最初の操作が完了していることを確認する必要があります。これは通常、コールバックを使用して行われます。

このことを考慮:

function doSomethingAsync (num) {
    console.log('Starting something ' + num);
    setTimeout(function () {
        console.log('Done doing something ' + num);
    }, 10);
}

function runEverything () {
    for (var i = 0; i < 3; i++)
        doSomethingAsync(i);
}

runEverything();

出力:

Starting something 0
Starting something 1
Starting something 2
Done doing something 0
Done doing something 1
Done doing something 2

ただし、コールバック構造を追加し、これらのコールバックを利用するためにループを置き換えると、次の実行を開始する前に、前の構造が終了するrunEverythingまで待機します。doSomethingAsync

function doSomethingAsync (num, callback) {
    console.log('Starting something ' + num);
    setTimeout(function () {
        console.log('Done doing something ' + num);
        callback();
    }, 10);
}

function runEverything () {
    var i = 0;
    var doneCallback = function () {
        if (++i < 3)
            doSomethingAsync(i, doneCallback);
    };

    doSomethingAsync(i, doneCallback);
}

出力:

Starting something 0
Done doing something 0
Starting something 1
Done doing something 1
Starting something 2
Done doing something 2

Node.js へようこそ。

于 2013-04-03T16:46:40.057 に答える
1

言及されていない解決策は、非同期ライブラリを利用することです。このモジュールを使用すると、すべての「checkFile」呼び出しを簡単にシリアル化できます。すべての呼び出しを効果的にキューに追加し、各呼び出しは前の呼び出しが完了するのを待ってから実行します。このソリューションの実行は少し遅くなる可能性がありますが、発生しているように見える制御フローの問題が発生することはありません。

于 2013-04-03T17:08:18.607 に答える