fs.watch срабатывает дважды, когда я меняю просматриваемый файл

 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
  • некоторые данные
  • Готово х 2

Это моя ошибка использования или ..?


person avicennasoftwarelabs    schedule 19.10.2012    source источник


Ответы (13)


fs.watch API:

  1. нестабилен
  2. имеет известное "поведение" в отношении повторные уведомления. В частности, случай Windows является результатом дизайна Windows, где один модификация файла может быть несколькими вызовами API Windows
person Pero P.    schedule 19.10.2012
comment
У меня была такая же проблема при тестировании на Windows. Я переключился на fs.watchFile() вместо fs.watch(), оба метода помечены как нестабильные, но это решило мою проблему. - person Drahcir; 09.04.2014
comment
У меня такая же проблема в среде Linux при редактировании целевого файла с помощью vim - person Keerthana Prabhakaran; 31.07.2018
comment
было бы лучше, если бы был предоставлен хотя бы альтернативный обходной путь. - person Varun Garg; 17.10.2018
comment
node-watch — лучший пакет с 0 зависимостями для того, чтобы снова сделать fs.watch отличным. - person zavr; 17.03.2019
comment
fs.watch сейчас в порядке (на 2019 год), событие изменения запускается один раз. - person Tien Do; 09.04.2019
comment
Пакет node-watch исправляет это. Хотя я получаю много нежелательных событий, они не дублируются. Это может быть связано с возвышенным текстом или каким-то плагином, который я установил. Я не понимаю, почему я получаю события обновления в папке .git, когда я не использую git, но в любом случае это другой вопрос. - person Rivenfall; 27.08.2019
comment
Все еще дважды срабатывает на Linux. - person electrovir; 18.09.2019

Я делаю поправку на это, делая следующее:

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
    }
}
person jwymanm    schedule 15.09.2013
comment
разрешение? Что ты имеешь в виду? - person David; 15.09.2013
comment
Underscore.js называет это debounce - person sshow; 27.12.2016

Я предлагаю работать с chokidar (https://github.com/paulmillr/chokidar), который намного лучше, чем fs.watch:

Комментируя свой README.md:

Node.js fs.watch:

  • Не сообщает имена файлов в OS X.
  • Вообще не сообщает о событиях при использовании таких редакторов, как Sublime в OS X.
  • Часто сообщает о событиях дважды.
  • Выдает большинство изменений как rename.
  • Имеет множество других проблем
  • Не предоставляет простой способ рекурсивного просмотра файловых деревьев.

Node.js fs.watchFile:

  • Почти так же плохо справляется с обработкой событий.
  • Также не обеспечивает никакого рекурсивного просмотра.
  • Приводит к высокой загрузке ЦП.
person Artisan72    schedule 09.10.2015

Если вам нужно следить за изменениями в файле, вы можете проверить мою небольшую библиотеку on- изменение файла. Он проверяет хеш файла sha1 между запускаемыми событиями change.

Объяснение того, почему у нас есть несколько запущенных событий:

В определенных ситуациях вы можете заметить, что одно событие создания генерирует несколько событий Created, которые обрабатываются вашим компонентом. Например, если вы используете компонент FileSystemWatcher для отслеживания создания новых файлов в каталоге, а затем тестируете его с помощью Блокнота для создания файла, вы можете увидеть два созданных события Created, даже если был создан только один файл. Это связано с тем, что Блокнот выполняет несколько действий с файловой системой во время процесса записи. Блокнот записывает на диск пакетами, которые создают содержимое файла, а затем атрибуты файла. Другие приложения могут работать таким же образом. Поскольку FileSystemWatcher отслеживает действия операционной системы, все события, запускаемые этими приложениями, будут перехватываться.

Источник

person Jan Święcki    schedule 10.04.2013
comment
@ImanMohamadi это зависит. Я думаю, что если файл меняется не слишком часто и размер файла не слишком велик, вам не о чем беспокоиться. Когда я использовал его, я не заметил проблем с производительностью. - person Jan Święcki; 04.06.2014
comment
Аккуратный! Спасибо, что поделились :) - person Antoine Cloutier; 04.08.2014
comment
Я добавил объяснение, которое нашел случайно. - person Jan Święcki; 04.03.2016
comment
Для большей производительности. мы можем использовать async/await с fsPromises узла. Вероятно, было бы излишним создавать ветвь для каждого процессора, который есть на машине, но это помогло бы для контрольных сумм. Может быть, другая контрольная сумма более эффективна. Мы также можем проверить размер файла перед любой контрольной суммой. Также может помочь добавление списка игнорирования, такого как .gitignore. - person Rivenfall; 27.08.2019

Мое индивидуальное решение

Лично мне нравится использовать 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);
};

Не стесняйтесь использовать этот пример, чтобы упростить код. Возможно, это НЕ лучше, чем использование модуля от других, но работает очень хорошо!

person Lasse Brustad    schedule 26.11.2017

Я впервые сталкиваюсь с этой проблемой, поэтому все ответы на данный момент, вероятно, лучше, чем мое решение, однако ни один из них не подходил на 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), потому что файл, который я просматриваю, гипотетически может очень часто получать законные обновления. И я не хотел использовать список просматриваемых файлов, как предлагает Эрик П, потому что я просматриваю только один файл. Решение Яна Свенцкого показалось излишним, поскольку я работаю с очень короткими и простыми файлами в среде с низким энергопотреблением. Наконец, ответ Бернадо заставил меня немного понервничать — второе обновление будет игнорироваться только в том случае, если оно придет до того, как я закончу обработку первого, а я не могу справиться с такой неопределенностью. Если бы кто-то оказался в этом очень конкретном сценарии, может быть, в подходе, который я использовал, есть какая-то ценность? Если с этим что-то не так, пожалуйста, дайте мне знать / отредактируйте этот ответ, но пока он работает хорошо?

ПРИМЕЧАНИЕ. Очевидно, что это настоятельно предполагает, что вы получите ровно 2 события для каждого реального изменения. Очевидно, я тщательно проверил это предположение и узнал его ограничения. На данный момент я подтвердил, что:

  • Изменение файла в редакторе Atom и сохранение запускает 2 обновления.
  • touch запускает 2 обновления
  • Перенаправление вывода через > (перезапись содержимого файла) вызывает 2 обновления
  • Добавление через >> иногда вызывает 1 обновление!*

Я могу придумать вполне веские причины для различного поведения, но нам не нужно знать, почему что-то происходит, чтобы спланировать это — я просто хотел подчеркнуть, что вы захотите проверить это сами в вашей собственной среде и в контексте ваших собственных вариантов использования (дух), а не доверять самопровозглашенному идиоту в Интернете. При этом, с предпринятыми мерами предосторожности, у меня пока не было никаких странностей.

* Полное раскрытие, я на самом деле не знаю, почему это происходит, но мы уже имеем дело с непредсказуемым поведением функции watch(), так что еще немного неопределенности? Для тех, кто следит дома, более быстрое добавление к файлу, кажется, приводит к тому, что он прекращает двойное обновление, но, честно говоря, я действительно не знаю, и меня устраивает поведение этого решения в реальном случае. быть использован, который представляет собой однострочный файл, который будет обновляться (содержимое заменяется), как минимум, два раза в секунду.

person Toadfish    schedule 26.09.2015

первый - изменить, а второй - переименовать

мы можем сделать разницу с функцией слушателя

function(event, 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

обратите внимание, что если вы хотите поймать все эти три евнета, посмотрите каталог исходного файла

person liandong    schedule 21.01.2013

Иногда я получаю несколько регистраций события Watch, в результате чего событие Watch срабатывает несколько раз. Я решил это, сохранив список отслеживаемых файлов и избегая регистрации события, если файл уже есть в списке:

 var watchfiles = {};

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

......

person Erik P    schedule 23.01.2014

Аналогичная/такая же проблема. Мне нужно было сделать некоторые вещи с изображениями, когда они были добавлены в каталог. Вот как я справился с двойной стрельбой:

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

Он будет игнорировать второй запуск до тех пор, пока не завершит работу с новым файлом.

person Bernardo SOUSA    schedule 13.07.2015

Как и в других ответах, говорится... У этого много проблем, но я могу справиться с этим следующим образом:

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

Надеюсь, я могу вам помочь...

person carlitoxenlaweb    schedule 04.02.2016

Самое простое решение:

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 () {
  // ...
})
person Arthur Ronconi    schedule 25.11.2017

Я загружал файл с помощью кукловода, и после сохранения файла я отправлял автоматические электронные письма. Из-за описанной выше проблемы я заметил, что отправляю 2 электронных письма. Я решил, остановив свое приложение с помощью process.exit() и автоматически запустив его с помощью pm2. Использование флагов в коде меня не спасло.

Если у кого-то возникнет эта проблема в будущем, можно использовать и это решение. Выйдите из программы и автоматически перезапустите с помощью инструментов монитора.

person Yunus    schedule 09.03.2020

Вот мое простое решение. Это работает хорошо каждый раз.

// 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)
  }
});
person Esteban Ortega    schedule 09.02.2021