Как последовательно запускать задачи Gulp один за другим

398

во фрагменте вот так:

gulp.task "coffee", ->
    gulp.src("src/server/**/*.coffee")
        .pipe(coffee {bare: true}).on("error",gutil.log)
        .pipe(gulp.dest "bin")

gulp.task "clean",->
    gulp.src("bin", {read:false})
        .pipe clean
            force:true

gulp.task 'develop',['clean','coffee'], ->
    console.log "run something else"

В developзадании, которое я хочу выполнить, cleanи после того, как оно выполнено, запустите, coffeeи когда это будет сделано, запустите что-то еще Но я не могу понять это. Эта часть не работает. Пожалуйста, порекомендуйте.

iLemming
источник
10
Модуль npm для последовательности выполнения теперь решает эту проблему - все остальные ответы теперь не имеют значения - см. Ответ
OverZealous
1
Gulp 4.0 изначально поддерживает выполнение задач в последовательности, что делает их run-sequenceустаревшими - см. Ответ
massanishi
Казалось бы, Gulp4 ломает больше вещей, чем исправляет. После нескольких часов борьбы я вернулся к 3.9.1. Я понимаю, что основные версии могут / сломают backcompat, но с загадочными и бесполезными сообщениями об ошибках, я говорю нет, спасибо. v4 не готов.
Сб Тиру

Ответы:

121

Это еще не официальный релиз, но выход Gulp 4.0 позволяет легко выполнять синхронные задачи с gulp.series. Вы можете просто сделать это так:

gulp.task('develop', gulp.series('clean', 'coffee'))

Я нашел хороший пост в блоге, в котором рассказывается, как обновить и использовать эти полезные функции: например, перейти на gulp 4

massanishi
источник
3
Метод выбора для всех новичков. Они действительно должны начать с gulp 4, пропуская все 3. * хлопоты и широкий спектр антипаттернов.
Металим
187
его 2017, и они до сих пор не представили его. Отлично.
Томаш Муларчик
14
Я не вижу смысла, правда. Если вам абсолютно необходимо, чтобы A запускался только после запуска B, то A зависит от B. Почему вы не можете просто указать, что B является зависимостью от A? gulp.task('coffee', ['clean'], function(){...}); gulp.task('develop', ['coffee']);
musicin3d
3
@ musicin3d То, что вы говорите, работает, но вы связываете одно задание с предыдущим. Например, я хочу, чтобы можно было строить без необходимости ворчать раньше. Это лучшее решение иметь независимые задачи и определять порядок выполнения с помощью внешнего инструмента.
AxeEffect
11
его 2018, и они наконец представили его. Отлично.
Даргмусли
411

По умолчанию gulp выполняет задачи одновременно, если они не имеют явных зависимостей. Это не очень полезно для таких задач, как clean, например , когда вы не хотите зависеть, но вам нужно, чтобы они выполнялись раньше всего.

Я написал в run-sequenceплагин специально , чтобы устранить эту проблему с глотком. После установки используйте его так:

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

gulp.task('develop', function(done) {
    runSequence('clean', 'coffee', function() {
        console.log('Run something else');
        done();
    });
});

Вы можете прочитать полные инструкции по пакету README - он также поддерживает запуск некоторых наборов задач одновременно.

Обратите внимание, что это будет (эффективно) исправлено в следующей основной версии gulp , так как они полностью устраняют автоматическое упорядочение зависимостей и предоставляют инструменты, аналогичные тем, run-sequenceкоторые позволяют вам вручную указывать порядок выполнения по своему усмотрению.

Тем не менее, это серьезное изменение, поэтому нет причин ждать, когда вы сможете использовать его run-sequenceсегодня.

чрезмерно усердный
источник
2
@OverZealous спасибо за плагин! Кстати, gulp-cleanплагин не реализовывал Streams 2, поэтому у него были проблемы с запуском в качестве зависимостей. Это было исправлено в версии 0.3.0, мой коллега представил PR, чтобы преобразовать его.
известноасиля
3
@Indolering Встроенная функциональность зависимости задачи не решает этот сценарий. Зависимые задачи не всегда выполняются: там нет встроенного в пути для выполнения двух задач подряд некоторые из времени, но не каждый раз. run-sequenceрешает критическую часть недостающей функциональности в gulp.
Превосходно
2
Кроме того, зависимости задач не являются полным решением. Скажем, у меня есть две задачи, которые независимо запускают тесты с использованием базы данных. Ни один из них не зависит от другого, но я не хочу, чтобы они выполнялись одновременно, потому что им обоим нужно использовать базу данных.
peterjwest
3
Удивительный модуль - Вот отличное объяснение того, зачем это нужно - blog.mdnbar.com/gulp-for-simple-build-proccess - Это должен быть принятый ответ
danday74
5
Я благодарен, что вы приняли решение для этого, но абсурдно, что нам нужны дополнительные инструменты для последовательного выполнения. Последовательное выполнение должно быть по умолчанию или, по крайней мере, чертовски легко сделать. Makefiles имеют последовательное выполнение в течение 40 лет. Экосистема JS делает меня больным. 73 мегабайта node_modules просто для компиляции стандартного проекта без каких-либо функций, и это все еще не включает в себя возможность последовательного выполнения. Операционные системы умещаются в меньшем пространстве и имеют ядра и драйверы FFS.
joonas.fi
371

Единственное хорошее решение этой проблемы можно найти в документации gulp, которую можно найти здесь.

var gulp = require('gulp');

// takes in a callback so the engine knows when it'll be done
gulp.task('one', function(cb) {
  // do stuff -- async or otherwise
  cb(err); // if err is not null and not undefined, the orchestration will stop, and 'two' will not run
});

// identifies a dependent task must be complete before this one begins
gulp.task('two', ['one'], function() {
  // task 'one' is done now
});

gulp.task('default', ['one', 'two']);
// alternatively: gulp.task('default', ['two']);
Матье Бордере
источник
6
По какой-то причине я ReferenceError: err is not definedпытаюсь выполнить эту gulp-compassзадачу, я что-то упустил?
вафля
11
@waffl в этом примере используется обратный вызов, который не единственный способ сделать это. Согласно документам , вы также можете «вернуть обещание или поток, который движок должен ждать, чтобы разрешить или завершить соответственно». Поэтому, если вы возвращаете поток в задаче 1, например return gulp.src('app/**/*.js').pipe(concat(app.js)).pipe(gulp.dest('app/scripts');, ключом является определение задачи 1 как зависимой при определении задачи 2: gulp.task('two', ['one'], function() {... задача 2 теперь будет ждать завершения задачи 1 перед выполнением.
Esvendsen
24
Это создает тесную связь между «один» и «два». Что делать, если вы хотите запустить «два» без «один»
wilk
5
Вы определяете новую задачу без зависимости?
Матье Бордере
8
Необходимость выполнения двух задач последовательно подразумевает тесную связь.
Ринго
56

Я сгенерировал приложение node / gulp с помощью Yeoman генератора generator-gulp-webapp . Он решал «чистую головоломку» таким образом (переводя к оригинальным задачам, упомянутым в вопросе):

gulp.task('develop', ['clean'], function () {
  gulp.start('coffee');
});
Стив
источник
2
Это было точно (и очень просто), в котором я нуждался. В нем рассматривается сценарий, в котором мне нужно сделать что-то вроде clean в качестве предыдущей зависимости, чтобы построить зависимости без установки clean в качестве зависимости от этих задач. PS: Информация о бите gulp.start () - предостережение emptor: github.com/gulpjs/gulp/issues/426
Jaans,
В этом есть смысл; обратный вызов после выполнения основной задачи (и ее зависимых задач). Спасибо.
Маркау
4
Если кому-то интересно, почему нет официальной документации gulp.start(), этот ответ от члена gulp.start is undocumented on purpose because it can lead to complicated build files and we don't want people using it
gulp
1
В этом случае, как я могу определить, что coffeeзадание выполнено? Если я не developcoffee
обнаружу
уже получил ответ от run-sequenceисходного кода: gulp.on('task_stop'). Подробности смотрите в моем расширенном ответе: stackoverflow.com/a/38818657/3027390
thybzi
32

последовательность выполнения является наиболее понятным способом (по крайней мере, до выпуска Gulp 4.0)

С помощью run-sequence ваша задача будет выглядеть так:

var sequence = require('run-sequence');
/* ... */
gulp.task('develop', function (done) {
    sequence('clean', 'coffee', done);
});

Но если вы (по какой-то причине) предпочитаете не использовать его, gulp.startметод поможет :

gulp.task('develop', ['clean'], function (done) {
    gulp.on('task_stop', function (event) {
        if (event.task === 'coffee') {
            done();
        }
    });
    gulp.start('coffee');
});

Примечание. Если вы только запускаете задачу без прослушивания результата, developзадача завершится раньше, чем может coffee, и это может сбить с толку.

Вы также можете удалить прослушиватель событий, когда он не нужен

gulp.task('develop', ['clean'], function (done) {
    function onFinish(event) {
        if (event.task === 'coffee') {
            gulp.removeListener('task_stop', onFinish);
            done();
        }
    }
    gulp.on('task_stop', onFinish);
    gulp.start('coffee');
});

Подумайте, есть ли task_errсобытие, которое вы можете захотеть послушать. task_stopсрабатывает при успешном завершении, а task_errпоявляется при возникновении ошибки.

Вы также можете удивиться, почему нет официальной документации для gulp.start(). Этот ответ от члена глотка объясняет вещи:

gulp.start специально не документирован, потому что это может привести к сложным файлам сборки, и мы не хотим, чтобы люди его использовали

(источник: https://github.com/gulpjs/gulp/issues/426#issuecomment-41208007 )

thybzi
источник
два кофе этому человеку! решение с удалением слушателя работает просто отлично!
daniel.bavrin
Это действительно ответ, или просто «глоток 4». Последовательность запуска является надежной.
LAdams87
26

Согласно документам Gulp:

Выполняются ли ваши задачи до завершения зависимостей? Убедитесь, что задачи зависимостей правильно используют подсказки асинхронного запуска: принять обратный вызов или вернуть обещание или поток событий.

Чтобы запустить последовательность задач синхронно:

  1. Возврат потока событий (например gulp.src) для gulp.taskинформирования задачи о том, когда поток заканчивается.
  2. Объявите зависимости задачи во втором аргументе gulp.task.

Смотрите пересмотренный код:

gulp.task "coffee", ->
    return gulp.src("src/server/**/*.coffee")
        .pipe(coffee {bare: true}).on("error",gutil.log)
        .pipe(gulp.dest "bin")

gulp.task "clean", ['coffee'], ->
      return gulp.src("bin", {read:false})
        .pipe clean
            force:true

gulp.task 'develop',['clean','coffee'], ->
    console.log "run something else"
senornestor
источник
4
ЭТО должен быть правильный ответ! Ответное задание сделало свое дело! Спасибо чувак.
Дзукр
10

У меня была точно такая же проблема, и решение оказалось для меня довольно простым. В основном измените свой код на следующий, и он должен работать. Примечание: возврат до gulp.src сделал все для меня.

gulp.task "coffee", ->
    return gulp.src("src/server/**/*.coffee")
        .pipe(coffee {bare: true}).on("error",gutil.log)
        .pipe(gulp.dest "bin")

gulp.task "clean",->
    return gulp.src("bin", {read:false})
        .pipe clean
            force:true

gulp.task 'develop',['clean','coffee'], ->
    console.log "run something else"
CPP
источник
Спасибо за примечание о возвращении! Я сходил с ума, пытаясь понять, почему глоток не справлялся с задачами.
Андрей Ф
Это должен быть правильный ответ, чтобы избежать тесной связи между задачами. Работает хорошо. gulp.series, доступный в 4.0, вероятно, лучший ответ, но на сегодняшний день 4.0 недоступен.
Уилк
Gulp
scape
8

перепробовал все предложенные решения, у всех вроде бы свои проблемы.

Если вы действительно посмотрите на источник Orchestrator, особенно на .start()реализацию, то увидите, что если последний параметр является функцией, он будет обрабатывать его как обратный вызов.

Я написал этот фрагмент для своих собственных задач:

  gulp.task( 'task1', () => console.log(a) )
  gulp.task( 'task2', () => console.log(a) )
  gulp.task( 'task3', () => console.log(a) )
  gulp.task( 'task4', () => console.log(a) )
  gulp.task( 'task5', () => console.log(a) )

  function runSequential( tasks ) {
    if( !tasks || tasks.length <= 0 ) return;

    const task = tasks[0];
    gulp.start( task, () => {
        console.log( `${task} finished` );
        runSequential( tasks.slice(1) );
    } );
  }
  gulp.task( "run-all", () => runSequential([ "task1", "task2", "task3", "task4", "task5" ));
Ассаф Молдавский
источник
4

Я долго искал этот ответ. Теперь я получил это в официальной документации глотка.

Если вы хотите выполнить задачу gulp, когда последняя будет завершена, вы должны вернуть поток:

gulp.task('wiredep', ['dev-jade'], function () {
    var stream = gulp.src(paths.output + '*.html')
        .pipe($.wiredep())
        .pipe(gulp.dest(paths.output));

    return stream; // execute next task when this is completed
});

// First will execute and complete wiredep task
gulp.task('prod-jade', ['wiredep'], function() {
    gulp.src(paths.output + '**/*.html')
        .pipe($.minifyHtml())
        .pipe(gulp.dest(paths.output));
});

Лучо Суарес
источник
3

Просто сделайте coffeeзависит от cleanи developзависит от coffee:

gulp.task('coffee', ['clean'], function(){...});
gulp.task('develop', ['coffee'], function(){...});

Отправка теперь серийная: cleancoffeedevelop. Обратите внимание, что cleanреализация и coffeeреализация должны принимать обратный вызов, «чтобы механизм знал, когда это будет сделано» :

gulp.task('clean', function(callback){
  del(['dist/*'], callback);
});

В заключение ниже приведен простой шаблон gulp для синхронной, cleanза которой следуют асинхронные зависимости сборки :

//build sub-tasks
gulp.task('bar', ['clean'], function(){...});
gulp.task('foo', ['clean'], function(){...});
gulp.task('baz', ['clean'], function(){...});
...

//main build task
gulp.task('build', ['foo', 'baz', 'bar', ...], function(){...})

Глоток достаточно умен, чтобы бежать cleanровно один раз заbuild , независимо от того, от скольких buildзависимостей зависит clean. Как написано выше, cleanэто барьер синхронизации, затем все buildзависимости работают параллельно, затем buildзапускаются.

akarve
источник
3

Для меня это не было выполнением задачи minify после конкатенации, поскольку она ожидает конкатенацию ввода, и она не генерировалась несколько раз.

Я попытался добавить к задаче по умолчанию в порядке выполнения, и это не сработало. Это сработало после добавления только returnдля каждой задачи и получения минимизации внутри, gulp.start()как показано ниже.

/**
* Concatenate JavaScripts
*/
gulp.task('concat-js', function(){
    return gulp.src([
        'js/jquery.js',
        'js/jquery-ui.js',
        'js/bootstrap.js',
        'js/jquery.onepage-scroll.js',
        'js/script.js'])
    .pipe(maps.init())
    .pipe(concat('ux.js'))
    .pipe(maps.write('./'))
    .pipe(gulp.dest('dist/js'));
});

/**
* Minify JavaScript
*/
gulp.task('minify-js', function(){
    return gulp.src('dist/js/ux.js')
    .pipe(uglify())
    .pipe(rename('ux.min.js'))
    .pipe(gulp.dest('dist/js'));
});

gulp.task('concat', ['concat-js'], function(){
   gulp.start('minify-js');
});

gulp.task('default',['concat']); 

Источник http://schickling.me/synchronous-tasks-gulp/

дэво
источник
2

Gulp и Node используют обещания .

Так что вы можете сделать это:

// ... require gulp, del, etc

function cleanTask() {
  return del('./dist/');
}

function bundleVendorsTask() {
  return gulp.src([...])
    .pipe(...)
    .pipe(gulp.dest('...'));
}

function bundleAppTask() {
  return gulp.src([...])
    .pipe(...)
    .pipe(gulp.dest('...'));
}

function tarTask() {
  return gulp.src([...])
    .pipe(...)
    .pipe(gulp.dest('...'));
}

gulp.task('deploy', function deployTask() {
  // 1. Run the clean task
  cleanTask().then(function () {
    // 2. Clean is complete. Now run two tasks in parallel
    Promise.all([
      bundleVendorsTask(),
      bundleAppTask()
    ]).then(function () {
      // 3. Two tasks are complete, now run the final task.
      tarTask();
    });
  });
});

Если вы возвращаете поток gulp, вы можете использовать then()метод для добавления обратного вызова. Кроме того, вы можете использовать родной Node Promiseдля создания своих собственных обещаний. Здесь я использую Promise.all()один обратный вызов, который срабатывает, когда все обещания разрешаются.

Питер Дж. Харт
источник
-8

Попробуйте это взломать :-) Gulp v3.x Hack for Async bug

Я попробовал все «официальные» способы в Readme, они не работали для меня, но это сработало. Вы также можете перейти на gulp 4.x, но я настоятельно рекомендую этого не делать, он ломает столько вещей. Вы могли бы использовать настоящее обещание JS, но эй, это быстро, грязно, просто :-) По сути, вы используете:

var wait = 0; // flag to signal thread that task is done
if(wait == 0) setTimeout(... // sleep and let nodejs schedule other threads

Проверьте пост!

Уилл Биттнер
источник
Есть лучшие способы решения проблемы, использование setTimeout не присваивается. Вы не могли точно знать, сколько времени займет выполнение задачи.
Реджинальдо Камарго Рибейро
Вы действительно должны подумать, прежде чем писать код pff
DDD