当我更改观看的文件时,fs.watch 触发了两次

Posted

技术标签:

【中文标题】当我更改观看的文件时,fs.watch 触发了两次【英文标题】:fs.watch fired twice when I change the watched file 【发布时间】:2012-10-10 08:15:20 【问题描述】:
 fs.watch( 'example.xml', function ( curr, prev ) 
   // on file change we can read the new xml
   fs.readFile( 'example.xml','utf8', function ( err, data ) 
     if ( err ) throw err;
     console.dir(data);
     console.log('Done');
   );
 );

输出:

一些数据 完成 X 1 一些数据 完成 X 2

这是我的使用错误还是..?

【问题讨论】:

【参考方案1】:

我遇到了同样的问题。如果不想触发多次,可以使用debounce函数。

 fs.watch( 'example.xml', _.debounce(function ( curr, prev ) 
   // on file change we can read the new xml
   fs.readFile( 'example.xml','utf8', function ( err, data ) 
     if ( err ) throw err;
     console.dir(data);
     console.log('Done');
   );
 , 100));

【讨论】:

【参考方案2】:

这是我的简单解决方案。每次都运行良好。

// Update obj as file updates
obj = JSON.parse(fs.readFileSync('./file.json', 'utf-8'));
fs.watch('./file.json', () => 
  const data = JSON.parse(fs.readFileSync('./file.json', 'utf-8') || '');
  if(Object.entries(data).length > 0)  // This checks fs.watch() isn't false-firing
    obj = data;
    console.log('File actually changed: ', obj)
  
);

【讨论】:

【参考方案3】:

我正在使用 puppeteer 下载文件,一旦保存文件,我就会自动发送电子邮件。由于上述问题,我注意到,我正在发送 2 封电子邮件。我通过使用 process.exit() 停止我的应用程序并使用 pm2 自动启动来解决。在代码中使用标志并没有救我。

如果将来有人遇到此问题,也可以使用此解决方案。退出程序并使用监控工具自动重启。

【讨论】:

【参考方案4】:

我的自定义解决方案

我个人喜欢使用return 来防止在检查某些东西时运行代码块,所以,这是我的方法:

var watching = false;
fs.watch('./file.txt', () => 
    if(watching) return;
    watching = true;

    // do something

    // the timeout is to prevent the script to run twice with short functions
    // the delay can be longer to disable the function for a set time
    setTimeout(() => 
        watching = false;
    , 100);
;

请随意使用此示例来简化您的代码。它可能比使用其他模块更好,但效果很好!

【讨论】:

【参考方案5】:

fs.watch api:

    是unstable 在重复通知方面有known "behaviour"。具体来说,windows case 是 windows 设计的结果,其中单个文件修改可以是对 windows API 的多次调用

【讨论】:

我在 Windows 上测试时遇到了同样的问题。我切换到fs.watchFile() 而不是fs.watch(),这两种方法都被标记为不稳定,但它解决了我的问题。 我在linux环境下用vim编辑目标文件时遇到同样的问题 如果至少有一个替代的解决方法会更好。 node-watch 是让 fs.watch 再次变得更好的最佳软件包,0 依赖项。 fs.watch 现在很好(2019 年),更改事件被触发一次。【参考方案6】:

最简单的解决方案:

const watch = (path, opt, fn) => 
  var lock = false
  fs.watch(path, opt, function () 
    if (!lock) 
      lock = true
      fn()
      setTimeout(() => lock = false, 1000)
    
  )

watch('/path',  interval: 500 , function () 
  // ...
)

【讨论】:

【参考方案7】:

如果您需要查看文件的更改,可以查看我的小型库on-file-change。它检查触发的change 事件之间的文件 sha1 哈希。

解释为什么我们有多个触发事件:

您可能会注意到在某些情况下,单个创建事件会生成多个由您的组件处理的 Created 事件。例如,如果您使用 FileSystemWatcher 组件监视目录中新文件的创建,然后使用记事本创建文件对其进行测试,即使只创建了一个文件,您也可能会看到生成了两个 Created 事件。这是因为记事本在写入过程中执行了多个文件系统操作。记事本分批写入磁盘,创建文件内容,然后创建文件属性。其他应用程序可以以相同的方式执行。因为 FileSystemWatcher 监视操作系统活动,所以这些应用程序触发的所有事件都会被拾取。

Source

【讨论】:

@ImanMohamadi 这取决于。我认为如果文件更改不是太频繁并且文件大小不是太大,您不必担心。当我使用它时,我没有注意到性能问题。 整洁!感谢分享:) 我已经添加了我偶然发现的解释。 更多性能。我们可以将 async/await 与节点的 fsPromises 一起使用。为机器拥有的每个 CPU 创建一个分叉可能有点过分,但它有助于校验和。也许另一个校验和更有效。我们还可以在任何校验和之前检查文件大小。添加 .gitignore 之类的忽略列表也会有所帮助。【参考方案8】:

就像其他人的回答所说...这有很多麻烦,但我可以这样处理:

var folder = "/folder/path/";

var active = true; // flag control

fs.watch(folder, function (event, filename) 
    if(event === 'rename' && active)  //you can remove this "check" event
        active = false;

        // ... its just an example
        for (var i = 0; i < 100; i++) 
            console.log(i);
        

        // ... other stuffs and delete the file
        if(!active)
            try 
                fs.unlinkSync(folder + filename);
             catch(err) 
                console.log(err);
            
            active = true
        
    
);

希望能帮到你...

【讨论】:

【参考方案9】:

我建议与chokidar (https://github.com/paulmillr/chokidar) 合作,这比fs.watch 要好得多:

评论其 README.md:

Node.js fs.watch:

不报告 OS X 上的文件名。 在 OS X 上使用 Sublime 等编辑器时根本不报告事件。 经常报告两次事件。rename 发出大多数更改。 有a lot of other issues 不提供递归查看文件树的简单方法。

Node.js fs.watchFile:

在事件处理方面几乎一样糟糕。 也不提供任何递归观察。 导致 CPU 利用率高。

【讨论】:

【参考方案10】:

我是第一次处理这个问题,所以到目前为止所有的答案都可能比我的解决方案更好,但是它们都不是 100% 适合我的情况,所以我想出了一些稍微不同的东西——我使用 XOR 操作翻转 0 和 1 之间的整数,有效地跟踪和忽略文件上的每一秒事件:

var targetFile = "./watchThis.txt"; 
var flippyBit = 0; 

fs.watch(targetFile, persistent: true, function(event, filename) 

      if (event == 'change')
        if (!flippyBit) 
          var data = fs.readFile(targetFile, "utf8", function(error, data) 
            gotUpdate(data);
          )
         else 
          console.log("Doing nothing thanks to flippybit.");              
        
        flipBit(); // call flipBit() function
      
    );

// Whatever we want to do when we see a change
function gotUpdate(data) 
    console.log("Got some fresh data:");
    console.log(data);
    


// Toggling this gives us the "every second update" functionality

function flipBit() 
    flippyBit = flippyBit ^ 1;

我不想使用与时间相关的函数(如 jwymanm 的回答),因为我正在观看的文件可能会非常频繁地获得合法更新。而且我不想使用 Erik P 建议的观看文件列表,因为我只看一个文件。 Jan Święcki 的解决方案似乎有点矫枉过正,因为我正在低功耗环境中处理极短且简单的文件。最后,Bernado 的回答让我有点紧张——如果它在我处理完第一个更新之前到达,它只会忽略第二个更新,我无法处理那种不确定性。如果有人发现自己处于这种非常具体的场景中,那么我使用的方法可能有一些优点吗?如果它有什么大问题,请让我知道/编辑这个答案,但到目前为止它似乎运作良好?

注意:显然,这强烈假设每次实际更改您将获得 2 个事件。显然,我仔细测试了这个假设,并了解了它的局限性。到目前为止,我已经确认:

在 Atom 编辑器中修改文件并保存触发器 2 更新 touch 触发 2 次更新 通过&gt; 的输出重定向(覆盖文件内容)触发2 次更新 通过&gt;&gt; 追加有时会触发 1 次更新!*

我可以为不同的行为想出完美的理由,但我们不需要知道为什么会发生某些事情来计划它 - 我只是想强调你会想要检查在你自己的环境和你自己的用例的上下文中为你自己(duh),不要相信互联网上自认的白痴。话虽这么说,但采取了预防措施,到目前为止我还没有任何奇怪的事情。

* 全面披露,我实际上不知道为什么会发生这种情况,但我们已经在使用 watch() 函数处理不可预测的行为,那么还有什么不确定性呢?对于在家跟随的任何人,更快速地附加到文件似乎会导致它停止双重更新,但老实说,我真的不知道,而且我对这个解决方案在实际情况下的行为感到满意它会使用,这是一个单行文件,最快每秒会更新(内容替换)两次。

【讨论】:

【参考方案11】:

类似/相同的问题。当图像被添加到目录时,我需要对它们做一些事情。以下是我处理双重射击的方法:

var fs = require('fs');
var working = false;

fs.watch('directory', function (event, filename) 
  if (filename && event == 'change' && active == false) 
    active = true;

    //do stuff to the new file added

    active = false;
);

它将忽略第二次触发,直到完成与新文件有关的操作。

【讨论】:

【参考方案12】:

我有时会收到多次注册 Watch 事件,导致 Watch 事件触发多次。 我通过保留观看文件列表来解决它,如果文件已经在列表中,则避免注册事件:

 var watchfiles = ;

function initwatch(fn, callback) 
    if watchlist[fn] 
        watchlist[fn] = true;
        fs.watch(fn).on('change', callback);
    

......

【讨论】:

【参考方案13】:

我通过以下方式考虑到这一点:

var fsTimeout

fs.watch('file.js', function(e) 

    if (!fsTimeout) 
        console.log('file.js %s event', e)
        fsTimeout = setTimeout(function()  fsTimeout=null , 5000) // give 5 seconds for multiple events
    

【讨论】:

津贴?什么意思? Underscore.js 称之为debounce【参考方案14】:

一是更改,二是重命名

我们可以从监听函数中有所作为

function(event, filename) 


侦听器回调获取两个参数(事件、文件名)。 event 是“rename”或“change”,filename 是触发事件的文件的名称。

// rm sourcefile targetfile
fs.watch( sourcefile_dir , function(event, targetfile))
    console.log( targetfile, 'is', event)

由于源文件被重命名为目标文件,它会调用三个事件作为事实

null is rename // sourcefile not exist again
targetfile is rename
targetfile is change

注意,如果你想捕获所有这三个 evnet,请查看源文件的目录

【讨论】:

以上是关于当我更改观看的文件时,fs.watch 触发了两次的主要内容,如果未能解决你的问题,请参考以下文章

wpf点击checkbox触发了两次

Lambda 函数在 DynamoDB 事件上触发了两次

当我通过鼠标单击更改 SwiftUI 列表的选择时,@Published 属性的 didSet 被调用了两次

为 kendo ui Combobox 触发了两次更改事件

在我的 iOS 中单击按钮时,事件触发了两次。应用程序

Android Fragment 创建了两次方向更改