Reputation: 4713
Ubuntu 12.04
Node v0.6.14
CoffeeScript 1.3.1
fs.watchFile coffee_eval, (e) ->
console.log e
result = spawn 'coffee', ['-bc', coffee_eval]
msg = ''
result.stderr.on 'data', (str) ->
msg+= str
result.stderr.on 'end', ->
console.log 'msg: ', msg
print "!! #{coffee_eval}\n"
Whole code on gist: https://gist.github.com/2621576
Every time I save a file which is watched, the main function is called twitce rather than once.
My Editor is Sumlime Text 2.
the output words can be see :
Upvotes: 3
Views: 5772
Reputation: 13620
This is an old question but a common one which needs more up to date answer: It is the editor updating the content multiple times.
First and foremost, it is advised to use fs.watch
instead. However you may experience same problem with fs.watch
but it does not fire same event multiple times for a single change, you get that because your editor is updating the file content multiple times.
We can test this using a simple node server which writes to file when it receives a request:
const http = require('http');
const fs = require('fs');
const path = require('path');
const host = 'localhost';
const port = 3000;
const file = path.join(__dirname, 'config.json');
const requestListener = function (req, res) {
const data = new Date().toString();
fs.writeFileSync(file, data, { encoding: 'utf-8' });
res.end(data);
};
const server = http.createServer(requestListener);
server.listen(port, host, () => {
fs.watch(file, (eventType, filename) => {
console.log({ eventType });
});
console.log(`Server is running on http://${host}:${port}`);
});
Visit the server and observe the output. You will see it logs single event:
Server is running on http://localhost:3000
{ eventType: 'change' }
If you edit the file in an editor like VSCode, you may see multiple events are logged.
That is because editors tend to use stream API for efficiency and read and write files in chunks which causes the change event fired multiple times depending on the chunk size and the file length.
Since writes have very short window of time, you can use stats to eliminate the duplicate events for trivial use cases:
let lastModified = 0;
fs.watch(file, (eventType, filename) => {
stat(file).then(({ mtimeMs }) => {
if ((mtimeMs - lastModified) > 50) {
lastModified = mtimeMs;
// Do your work here! It will run once!
console.log({ eventType, filename });
}
});
});
Otherwise it is better to use debounce.
let timeoutId;
fs.watch(file, (eventType, filename) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
console.log({ eventType });
}, 100);
});
Check this anwswer to see how you can use it with other fs methods: https://stackoverflow.com/a/75149864/7134134
Upvotes: 2
Reputation: 11069
The problem has been fixed (at least) with fs.watch
now. I didn't try fs.watchFile
because it's not recommended by NodeJS documentation.
change
event each time file is modifiedrename
events when you changed a file name, one for current name and one for new namerename
event for newly created file (copied from other location) or deleted file.My environment: macOS 10.12.6 and NodeJS v11.10.1
Upvotes: 0
Reputation: 11069
It isn't directly related with the original question, but do you know what this sentence mean? (from fs.watch
documentation).
Also note the listener callback is attached to the 'change' event fired by fs.FSWatcher, but it is not the same thing as the 'change' value of eventType.
Upvotes: 0
Reputation: 1379
The problem is still present, here is the way I have found.
var actionDone = {}
fs.watch('.', function(x,filename) {
var path = './'+filename;
var stats = fs.statSync(path);
let seconds = +stats.mtime;
if(actionDone[filename] == seconds) return;
actionDone[filename] = seconds
//write your code here
});
We check if the last modified time is different before to continue.
Upvotes: 5
Reputation: 3600
Another suggestion for an npm module which is much better than fs.watch
or fs.watchFile
:
https://github.com/paulmillr/chokidar/
Upvotes: 0
Reputation: 1152
I solved this problem by flipping an 'ignore' flag from false to true every time an update was received, thereby ignoring every second event. BUT i also found that sometimes, a change to the file only resulted in one update. I'm not sure what causes this but it seemed to happen when updates were very frequent, and when those updates were appends (>>
). I did not observe any instances of a single change triggering more than two events.
There's more discussion of the issue in this question. I also posted some example code for my solution there.
Upvotes: 0
Reputation: 78
If you are using underscore or lodash, you could consider using throttle and discard the calls on the trailing edge. A basic example would be
var fs = require('fs');
var _ = require("lodash");
function FileWatcher (fileName)
{
this.file = fileName;
this.onChange = _.throttle(this.trigger, 100, {leading: false});
}
FileWatcher.prototype.observe = function ()
{
fs.watch(this.file, this.onChange);
}
FileWatcher.prototype.trigger = function ()
{
console.log("file changed :)");
}
var fileToWatch = __dirname + "/package.json";
new FileWatcher(fileToWatch).observe();
Upvotes: 1
Reputation: 6424
To solve this problem, I keep track of the previous "file modified" timestamp and don't run my normal callback code if the value is the same.
var filename = "/path/to/file";
var previousMTime = new Date(0);
var watcher = fs.watch(filename, {
persistent: false
}, function(){
fs.stat(filename, function(err, stats){
if(stats.mtime.valueOf() === previousMTime.valueOf()){
console.log("File Update Callback Stopped (same revision time)");
return;
}
previousMTime = stats.mtime;
// do your interesting stuff down here
});
});
Upvotes: 0
Reputation: 25555
I would suggest trying node-inotify-plusplus (https://github.com/coolaj86/node-inotify-plusplus) which has worked much better for me than fs.watchFile or fs.watch.
Upvotes: 1
Reputation: 38543
fs.watchFile is unstable. From the node docs:
fs.watchFile(filename, [options], listener)#
Stability: 2 - Unstable. Use fs.watch instead, if available.
You can try fs.watch
, but unfortunately it may suffer from the same problem. I had the same issue with fs.watch
on windows, when trying to create a similar monitor script.
The workaround was to log the time when the modification occurs and ignore the second change if it was triggered withing a few milliseconds. A bit ugly but it worked.
Upvotes: 6