Øyvind
Øyvind

Reputation: 849

Gulp.js, watch task runs twice when saving files

Given the following piece of code from my gulpfile.js, everytime I save or change a file, the task runs twice instead of one single time, why is that? I just want it to run one time.

var gulp = require('gulp');

gulp.task('default', function() {

  gulp.watch('server/**/*.js', function(){
    console.log('This runs twice everytime I change/save a javascript file located at server/**/*.js');
  }); 

});

I have also experienced the same with grunt and the plugin called grunt-contrib-watch.

Upvotes: 9

Views: 12868

Answers (12)

Peheje
Peheje

Reputation: 14244

I tried debounce and awaitWriteFinish. It didn't work. This did:

const gulp = require("gulp");
const exec = require('child_process').exec;
let run = false;

gulp.task("watch", () => {
  console.log("watching ..");
  gulp.watch("**/*.js", ["browserify"]);
});

gulp.task("browserify", () => {
  if (run) { return; }
  run = true;
  console.log("calling browserify");
  exec("browserify app.js -o bundle.js");
  setTimeout(() => {
    run = false;
  }, 1000);
});

Upvotes: 0

Alex
Alex

Reputation: 925

Had the same issue and it turns out, that gulp-sourcemaps causes it (see -> Using source maps causes task to run twice)

Get a solution with gulp-notify and the attribute onLast:

.pipe(notify({message: 'YOUR MESSAGE', onLast: true}));

Upvotes: 1

atilkan
atilkan

Reputation: 5056

I write here my experience, maybe helps someone. This cost me 1 week to detect. I made every possible debug. Removed files, reinstalled clean node packages. Removed Sublime plugins. Reported as a bug to Sublime github and plugins github pages.

My solution Close dropbox if open. I use Dropbox for my project and work there. When Dropbox is open, tasks run twice because Dropbox detects file change and does something. Specially Typescript files, i don't know why.

Upvotes: 0

Trisha Crowley
Trisha Crowley

Reputation: 101

This worked for me

.pipe(watch('/**/*.less', { awaitWriteFinish: true }))

https://github.com/paulmillr/chokidar#api

Upvotes: 10

mnzaki
mnzaki

Reputation: 15

Aside from editor specific solutions, I wrote a little gulp plugin that solves the problem. You can use gulp-debounce like so:

npm install --save-dev gulp-debounce

var debounce = require('gulp-debounce'),
    watch    = require('gulp-watch'),
    through  = require('through2');

gulp.watch('server/**/*.js')
.pipe(debounce({ wait: 1000 }))
.pipe(through.obj(function(vinyl) {
  console.log("this won't fire multiple times in 1000ms", vinyl.path);
});

Upvotes: 0

MattDiMu
MattDiMu

Reputation: 5003

Hint: the debounce parameter only works for the SAME file/event. If multiple events/files change, it won't help. Sometimes (e.g. I copied files into the a directory being served by my local server) gulp-cached might help, sometimes excluding certain files/patterns (e.g. the sourcemaps files) might help (use ! to negate the selection). e.g.

gulp.watch(['js/**/*', '!js/**/*.map'])

Upvotes: 2

Stphane
Stphane

Reputation: 3476

One year later ...

Using

  • nodejs 0.10.25
  • gulp 3.8.10

Gaz debounceDelay option did not change anything for me, neither did I understand how to use gulp-batch callback argument :/ ...

To avoid consecutives task calls after several files have been changed, I used the oldschool setTimeout function:

// ...
var
SRC = ['js/*.js', '!js/*.min.js'], DEST = 'js',
processJs = function(){
    util.log('Processing: ['+ SRC.join(' | ') +']');
    var stream = gulp.src(SRC)
        .pipe(uglify())
        .pipe(concat('scripts.min.js'))
        .pipe(gulp.dest(DEST));
    return stream;
};


gulp.task('default', function(){
    var delay = 2000, timer,
        watcher = gulp.watch(
            SRC,
            // Callback triggered whatever the event type is (added / changed / deleted)
            function(event) { // .path | .type
                // If the timer was assigned a timeout id few time ago..
                // Prevent last postpone task to be run
                if(timer){
                    clearTimeout(timer);
                }
                // Postpone the task
                timer = setTimeout(
                    function(){processJs();timer=0;}
                    ,delay
                );
            }
        );
    util.log("/!\\ Watching job "+ Array(25).join('~'));
});

Upvotes: 1

Mushr00m
Mushr00m

Reputation: 2356

I add the same problem in Espresso and you can "fix" it in Gulp by using the debounceDelay option like this :

gulp.watch('/**/*.less', {debounceDelay: 2000}, ['less']);

That did the trick for me. But it's a pain to add it to every gulp.watch I don't know if we can put this option globaly...

Upvotes: 13

Ryan Atallah
Ryan Atallah

Reputation: 2987

The problem is occurring because your editor, in this case Coda 2, is modifying the file twice on save. The same problem occurs in vim because of how vim creates buffer backups on save.

The solution to the problem in vim is to add

set nowritebackup

to your ~/.vimrc file. This changes the default save protocol to only make one edit to the original file.

In other words, the default save protocol is as follows:

  1. Write the buffer to the backup file
  2. Delete the original file
  3. Rename the backup to the name of the original file

And adding set nowritebackup simply replaces the original file on save. This protocol exists to reduce risk of data loss in the event of an I/O error on save.

Upvotes: 13

LukeAskew
LukeAskew

Reputation: 83

I was seeing a similar issue, but it was caused by having the page open in multiple tabs/windows.

Upvotes: 1

Øyvind
Øyvind

Reputation: 849

Seems like the answer to this question is a feature of the editor that was used, Coda 2. Based on some of the comments here and testing with multiple editors, it seems like Coda 2 saves a temporary file or similar and that causes the gulp watch function to be run twice.

I have not found a viable solution to this when using Coda 2, ended up with switching to Sublime Text 3.

Upvotes: 0

OverZealous
OverZealous

Reputation: 39580

You should be able to use gulp-batch to batch the changes, since it has a debounce option.

Something like this:

gulp.src(['server/**/*.js'], batch({debounce: 50}, function(events) {
    return events
        .pipe(...); // your code here
}));

Upvotes: 2

Related Questions