0

我来自 PHP 背景,试图围绕“事件驱动”的 Node.js 环境。我编写了一个小脚本,它从目录中读取文件并使用节目名称、季节和剧集编号(如果它们晚于数据库中当前存储的内容)更新 Redis。看来我遇到了一个异步问题,我无法完全理解 Redis DB 的标题是“My Show”、“05”季和“01”的标题。有两个文件被读取,其中包含“My Show S05E02”和“My Show S05E01”。

仅当季节/剧集晚于当前季节/剧集时,数据库才应更新,但是由于“updateTitle”的调用速度非常快,并且由于某种原因,“My Show S05E02”在“My Show S05E01”之前传递,因此更新功能始终将这两个值与原始值“My Show S05E01”进行比较,因此它使用 E02 更新 Redis,然后再次使用 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 发出第二次获取请求
  5. 首先从 Redis 中获取回报。
  6. 第二次从 Redis 中获取回报。
  7. 执行第一个设置为 Redis。
  8. 对 Redis 执行第二组。

如果第一个操作取决于第二个操作的结果,则需要确保第一个操作在开始第二个操作之前完成,这通常使用回调来完成。

考虑一下:

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

这可能不是您问题的最佳解决方案,但它可能是最简单的。

你是对的 - 它被设置回 E01 的原因是由于竞争条件。它试图将这一集设置为 02,然后转到下一个文件,即 01,但它还没有赶上并完成将其设置为 02,因此它读取为 01。这是有问题的行:

if ((season == cur_season && episode >= cur_episode) || season > cur_season)

如您所见,您正在检查 if episode >= cur_episode。将其更改为episode > cur_episode,您至少应该解决在这种特定情况下倒退的情节。如果当前情节是 01,您可能仍然会遇到问题,它先读取 03,然后是 02,并且在写入 03 之前,02 会覆盖 03。您可以尝试阅读所有更改的情节,然后检查它们并确定哪个是最后一个在应用程序中,而不是重复地从 redis 写入和读取。

你真的想解决竞争条件,这将解决一般情况并使你成为一个更强大的 Node 程序员,但是你会遇到无数不同的方法来解决这个问题(延迟、回调、承诺等)。 ..) 这远远超出了本次讨论的范围。我建议你阅读继续学习,祝你好运!

于 2013-04-03T16:24:59.393 回答
1

尚未提及的解决方案是使用异步库。使用此模块,您可以轻松地序列化所有“checkFile”调用。您将有效地将所有调用添加到队列中,每个调用在执行之前等待前一个调用完成。尽管此解决方案运行速度可能会慢一些,但您不应遇到任何您似乎遇到的控制流问题。

于 2013-04-03T17:08:18.607 回答