Gulp.js,保存文件时watch任务运行两次

9

以下是我gulpfile.js的代码片段,每当我保存或更改文件时,任务运行两次而不是一次,为什么?我只想让它运行一次。

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

});

我曾经遇到过 grunt 和名为 grunt-contrib-watch 的插件的同样问题。

1
用完全相同的代码,它只在我这里运行一次。您是否使用编辑器在保存时会触及另一个文件?您使用的是哪个平台?您的gulpfile中还有什么?那是哪个版本的gulp? - Mangled Deutz
我的猜测是你的编辑器先保存一个临时文件,然后再重命名它,这会触发两次任务。 - Paul Mougel
这是我的gulpfile中唯一的内容,我还有另一个gulpfile用于其他项目,在那里也遇到了同样的问题。我使用的是gulp版本3.5.1。我坐在MacBook Pro上,我的编辑器是Coda 2。你对我的编辑器的提示让我尝试在Webstorm中使用相同的文件,似乎只运行了一次。 - Øyvind
Coda 2有什么解决此问题的技巧吗? - Øyvind
你需要忽略一个临时文件夹吗? - robrich
我不确定,因为我不知道Coda 2如何处理文件保存。如果我知道项目中有一个临时文件夹,Coda 2会将临时文件保存到其中,那么我的解决方案可能是让gulp以某种方式忽略该文件夹。 - Øyvind
12个回答

13

我在Espresso中遇到了同样的问题,你可以使用debounceDelay选项在Gulp中进行“修复”,方法如下:

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

这对我很有帮助。但是为每个 gulp.watch 添加它很麻烦,不知道是否可以全局设置此选项...


1
对我来说,这个选项会等待2秒钟,然后触发处理程序两次。 - Yanick Rochon
@YanickRochon 如果他使用了 readDelay 选项,你的说法就是正确的,但去抖动是不同的。去抖动背后的思想是:当事件被触发时,将事件处理程序排队,但在调用它之前等待一定的时间。如果在超时期间再次触发事件,则丢弃当前排队的处理程序,排队一个新的处理程序,并重新启动计时器。在基本情况下,这将不断重复,处理程序实际上永远不会被调用,直到事件在超时期间停止触发,此时处理程序将被精确地调用一次。 - Ken Bellows

13

问题出现的原因是您的编辑器,例如 Coda 2,在保存时会对文件进行两次修改。由于 vim 在保存时创建缓冲区备份,因此也会出现同样的问题。

解决 vim 中的问题的方法是添加

set nowritebackup

将以下代码添加到您的~/.vimrc文件中。这将更改默认保存协议,仅对原始文件进行一次编辑。
换句话说,默认的保存协议如下:
  1. 将缓冲区写入备份文件
  2. 删除原始文件
  3. 将备份文件重命名为原始文件的名称
添加set nowritebackup只是在保存时替换原始文件。该协议旨在减少保存时I/O错误导致数据丢失的风险。

1
谢谢您阐明了为什么会发生这种情况,但事实上我并不满意这个解决方案。 - tremby
我尝试了 set backupcopy=yes,但无济于事,所以当我遇到这个设置时,我非常有希望!但是对我来说仍然失败了(无论是否启用了备份设置)。我使用的是OSX 10.11.2(El Capitan)和vim v.7.3。还有其他想法吗? - ekkis
谢谢您!我从fswatch的输出中看到了发生的情况,但不知道如何阻止它发生。 - Felix Schlitter
我使用Sublime,它做了类似的事情。至少我走在了正确的道路上。有没有Sublime的解决方案? - atilkan

10

2

由于 gulp-batch 具有 debounce 选项,因此您应该能够使用它来批量更改。

可以像这样进行操作:

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

恐怕那没有帮助。同时,gulp-batch似乎也解决了另一个问题。我认为解决方案可能在编辑器内部或者gulp使用的其他模块中,例如gaze或类似的工具。 - Øyvind
你试过调整去抖时间了吗? - OverZealous
我尝试了,但实际上在批处理中运行这些内容也遇到了问题。为澄清起见,我没有使用gulp-watch插件,只是使用内置的watch功能。我在Coda 2中使用grunt-contrib-watch时也遇到了同样的问题。我认为这两个插件在幕后使用相同的模块。很遗憾,因为这个小问题,我不得不更换编辑器,但这很重要,我不想每次运行时都要运行两次单元测试等内容。 - Øyvind
这个gulp-batch选项现在被称为timeout。像这样使用它batch({timeout: 500}, function(events) { ... })timeout的默认值为100毫秒,这应该已经足够时间让编辑器完成写入和重命名。 - Rory O'Kane

2

提示: debounce参数只对同一文件/事件起作用。如果多个事件/文件发生变化,它将无济于事。有时(如我将文件复制到由本地服务器提供的目录中) gulp-cached 可能有所帮助,有时排除某些文件/模式(例如源映射文件)可能会有所帮助(使用!来否定选择)。例如:

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

1
源映射是导致我多次重新加载的原因,与时间无关。所以谢谢你! - ErikAGriffin

1

一年后...

使用:

  • nodejs 0.10.25
  • gulp 3.8.10

Gaz的debounceDelay选项对我没有产生任何影响,我也不知道如何使用gulp-batch回调参数:/ ...

为了避免在多个文件被更改后连续调用任务,我使用了老式的setTimeout函数:

// ...
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('~'));
});

2
旁注:gulp-batch是一件未记录烂货;与gulp-watch本身相当,但经过一些实验我让它运行起来了。 - Arek Bal

1

我遇到了相同的问题,结果发现是gulp-sourcemaps引起的(请参见->使用源映射会导致任务运行两次

使用gulp-notify并添加onLast属性可以解决问题:

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

1

我遇到了类似的问题,但是它是由于在多个选项卡/窗口中打开页面所导致的。


0

除了特定于编辑器的解决方案外,我编写了一个小型的 gulp 插件来解决这个问题。您可以像这样使用 gulp-debounce

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

0

我在这里写下我的经验,希望能帮助到某些人。这花费了我一周的时间来检测。我进行了所有可能的调试。删除文件,重新安装干净的节点包。删除Sublime插件。向Sublime github和插件github页面报告了一个错误。

我的解决方案 如果打开了Dropbox,请关闭它。 我使用Dropbox进行项目和工作。当Dropbox打开时,任务会运行两次,因为Dropbox检测到文件更改并执行某些操作。特别是TypeScript文件,我不知道为什么。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接