Async.js 与 Gulp 轮流错误 "Callback was already called"

Async.js with Gulp turns error "Callback was already called"

我正在使用 async.js 以某种有序顺序加载我的 gulp 任务,所以我这样做:

// Gulp Default Build Task
gulp.task('default', function() {
    var tasks = ['sass_dev', 'browserify', 'lint', 'watch'];
    var sync = tasks.map(function(task) {
        return function(callback) {
            gulp.start(task, function(err) {
                if (err || task.length === 0) {
                    callback(err);
                } else {
                    callback(null, task[0]);
                }
            });
        };
    });
    async.series(sync);
});

任务 运行 正确,但在第一次更改脚本后我遇到了一个错误 if (fn === null) throw new Error("Callback was already called.");。我知道那是因为它调用了两次回调,但即使在 err 条件下它也无法正常工作。有人可以帮忙吗?

有一个更简单的方法。

'use strict';

let gulp = require('gulp');
let concat = require('gulp-concat');
let minifyCSS = require('gulp-minify-css');
let browserify = require('browserify');
let vsource = require("vinyl-source-stream");
let babel = require('babelify');

let source = {
 appjs: './ui-src/app.js',
 js: ['./ui-src/**/*.js'],
 libjs: ['./ui-src/lib/primus/primus.js'],
 appcss: ['./ui-src/css/*.css'],
 apphtml: ['./ui-src/**/*.html'],
 appimg: ['./ui-src/img/*']
};

gulp.task('appjs', function(){
 browserify({ debug: true })
  .transform(babel.configure({stage: 0}))
  .require(source.appjs, { entry: true })
  .bundle()
  .pipe(vsource('app.min.js'))
  .pipe(gulp.dest('./ui-dist'));
});

gulp.task('libjs', function () {
 gulp.src(source.libjs)
  .pipe(concat('lib.min.js'))
  .pipe(gulp.dest('./ui-dist'))
});

gulp.task('appcss', function () {
 gulp.src(source.appcss)
  .pipe(concat('app.min.css'))
  .pipe(minifyCSS())
  .pipe(gulp.dest('./ui-dist'))
});

gulp.task('apphtml', function() {
 gulp.src(source.apphtml)
  .pipe(gulp.dest('./ui-dist'));
 gulp.src(source.appimg, {base: 'ui-src'})
  .pipe(gulp.dest('./ui-dist'));
});

gulp.task('watch', function() {
 gulp.watch(source.appcss, ['appcss']);
 gulp.watch(source.apphtml, ['apphtml']);
 gulp.watch(source.js, ['appjs']);
});

gulp.task('default', ['appjs', 'appcss', 'apphtml', 'watch']);

gulp.task('nw', ['appjs', 'libjs', 'appcss', 'apphtml']);

问题

您 运行 陷入此问题的原因是您正在使用 Gulp 的一部分,这实际上不是 Gulp 的最终用户使用的。这是一个重现您报告的行为的小 gulpfile:

var gulp = require("gulp");

gulp.task('foo');

gulp.task('default', function () {
    gulp.start('foo', function () {
        console.log("callback!");
    });
    setTimeout(function () {
        gulp.start('foo');
    }, 1000);
});

运行 这将在控制台上产生:

[12:23:48] Using gulpfile /tmp/t15/test.js
[12:23:48] Starting 'default'...
[12:23:48] Starting 'foo'...
[12:23:48] Finished 'foo' after 66 μs
[12:23:48] Finished 'default' after 1.24 ms
callback!
[12:23:49] Starting 'foo'...
[12:23:49] Finished 'foo' after 8.99 μs
callback!

你看到 callback! 出现了两次,尽管 gulp.start 只用回调调用了一次!问题是 Gulp 记住回调并在 gulp.start 的第二次调用中使用它。

如果您想要详细信息,请查看 orchestrator,这是 Gulp 使用的内容。查找 doneCallback 及其管理方式。基本行为是,如果 start 被回调调用,那么它会删除 doneCallback 的任何先前值,但如果它没有被回调调用,那么旧值将保持不变并实际上被重用通过随后调用 start.

安全的解决方案

总之。看来您想 运行 按顺序执行一堆任务。我过去这样做的方法是声明将按顺序 运行 其他任务的任务,以便:

  1. 它的依赖关系是顺序运行任务的所有依赖关系的并集。

  2. 它的实现函数依次运行调用它应该执行的所有任务的实现函数

这是一个简短的例子:

var gulp = require("gulp");

gulp.task('x');
gulp.task('y');
gulp.task('q');

// We are defining task `a`, with dependencies `x` and `y`.
var a_deps = ['x', 'y'];
function a() {
    console.log("a");
}
gulp.task('a', a_deps);

// We are defining task `b`, with dependencies `x` and `q`.
var b_deps = ['x', 'q'];
function b() {
    console.log("b");
}
gulp.task('b', b_deps);

// We are defining task `default`, which is equivalent to running tasks
// `a` and `b` in sequence.
gulp.task('default', a_deps.concat(b_deps), function () {
    a();
    b();
});

郑重声明,我没有使用 run-sequence,因为当我尝试它时,我看到它 运行 多次绑定我的依赖项,这对我来说是无法接受的。

当 Gulp 4 出来时,我们将能够使用 gulp.series 对任务进行排序

实际上我使用了其他更轻量级的库 run-sequence,它运行良好且流畅。所以这是示例解决方案:

var sequence    = require('run-sequence');

gulp.task('default', function(callback) {
    sequence('sass_dev', ['browserify', 'lint'], 'watch', callback);
});
gulp.task('prod', function(callback) {
    sequence('sass_dev', ['browserify'], 'uglify', callback);
});

npm link