Як послідовно виконувати завдання 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і коли це буде зроблено, запустіть щось інше. Але я не можу цього зрозуміти. Цей твір не працює. Порадьте, будь ласка.


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 на прикладі


3
Метод вибору для всіх новачків. Вони дійсно повинні починатись із глоткою 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 рік, і вони нарешті його представили. Чудово.
dargmuesli

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, щоб перетворити його.
knownasilya

3
@Indolering Вбудована функціональна залежність завдань не вирішує цей сценарій. Завжди виконуються залежні завдання: не існує побудованого способу для виконання двох завдань поспіль деякий час, але не кожен раз. run-sequenceвирішує критичну частину відсутньої функціональності в глотці.
OverZealous

2
Також залежності завдань не є повноцінним рішенням. Скажіть, у мене є дві задачі по глоткові, які самостійно виконують тести за допомогою бази даних. Жоден із них не залежить від іншого, але я не хочу, щоб жоден з них працював одночасно, оскільки їм обом потрібно використовувати базу даних.
peterjwest

3
Дивовижний модуль - Ось чудове пояснення того, для чого це потрібно - blog.mdnbar.com/gulp-for-simple-build-proccess - Це має бути прийнята відповідь
da7374

5
Я вдячний, що ви вирішили це, але це абсурдно, що нам потрібні додаткові інструменти для отримання послідовного виконання. Послідовне виконання повинно бути за замовчуванням або, принаймні, ебать просто. Makefiles мали послідовне виконання протягом 40 років. Екосистема JS мене нудить. 73 мегабайти node_модулів просто для складання проекту панелі котлів без будь-яких особливостей, і це все ще не включає можливість послідовного виконання. Операційні системи вміщуються в менший простір, і вони мають ядра та драйвери FFS.
joonas.fi

371

Єдине хороше рішення цієї проблеми можна знайти в документації із залпом, яку можна знайти тут

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завданням, чи щось мені не вистачає?
waffl

11
@waffl у цьому прикладі використовується зворотний виклик, що це не єдиний спосіб зробити це. Згідно з документами , ви також можете "повернути обіцянку або потік, що двигун повинен чекати, щоб вирішити або закінчити відповідно". Отже, якщо ви повернете потік у першому завданні, наприклад return gulp.src('app/**/*.js').pipe(concat(app.js)).pipe(gulp.dest('app/scripts');, ключ полягає у визначенні завдання однієї як залежної при визначенні завдання другої: gulp.task('two', ['one'], function() {... Завдання два тепер чекатиме завершення завдання перед запуском.
есвендсен

24
Це створює тісний зв'язок між "одним" та "двома". Що робити, якщо ви хочете запустити "два" без запуску "один"
wilk

5
Ви визначаєте нове завдання без залежності?
Матьє Борде

8
Необхідність двох завдань для послідовного запуску передбачає щільне з'єднання.
Рінго

56

Я створив додаток node / gulp за допомогою генератора Yeoman generator -gulp- webapp. Він обробляв "чисту головоломку" таким чином (перекладаючи на оригінальні завдання, згадані в питанні):

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

2
Це було саме (і дуже просто), що мені було потрібно. Він стосується сценарію, коли мені потрібно зробити щось на кшталт "чистого", як попередня залежність, щоб створити залежності, не встановлюючи "чистим" як залежність від цих завдань. PS: Інформація про біт gulp.start () - caveat emptor
Яан

Що має сенс; зворотний виклик після завершення основного завдання (і його залежних завдань). Дякую.
markau

4
Якщо хтось цікавиться, чому немає офіційної документації gulp.start(), ця відповідь від члена gulp пояснює, що: gulp.start is undocumented on purpose because it can lead to complicated build files and we don't want people using it(джерело: github.com/gulpjs/gulp/isissue/426#issuecomment-41208007 )
thybzi

1
У цьому випадку як я можу виявити, що coffeeзавдання закінчено? Якщо я цього не виявляю, developзавдання закінчуватимуться ранішеcoffee
thybzi

вже отримав відповідь від run-sequenceвихідного коду: gulp.on('task_stop'). Дивіться мій розширений відповідь на деталі: stackoverflow.com/a/38818657/3027390
thybzi

32

послідовність запуску є найбільш зрозумілим способом (принаймні, до виходу Gulp 4.0)

З послідовністю запуску ваше завдання буде виглядати так:

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 пояснює:

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

(джерело: https://github.com/gulpjs/gulp/isissue/426#issuecomment-41208007 )


дві кави цьому чоловікові! рішення з видаленням слухача працює просто ідеально!
daniel.bavrin

Це справді відповідь, або просто, "гулп 4". Послідовність виконання є надійною.
LAdams87

26

Згідно з документами Gulp:

Чи виконуються ваші завдання до того, як залежність буде повна? Переконайтесь, що у Ваших завданнях залежності правильно використовуються підказки запуску async: приймати зворотний виклик або повертати потік обіцянки чи події.

Щоб синхронно запустити послідовність завдань:

  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"

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"

Дякуємо за записку про повернення! Збирався з глузду, намагаючись розібратися, чому глоток не вирішував завдання.
Ендрю Ф

Це має бути правильною відповіддю, щоб уникнути тісного зв’язку між завданнями. Добре працює. gulp.series, доступні в 4.0, мабуть, найкраща відповідь, але на сьогоднішній день 4.0 не доступний.
wilk

gulp development буде запускатись чистим чи спочатку кавою
scpe

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.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(){...})

Gulp досить розумний, щоб запустити cleanрівно один раз на buildнезалежність, незалежно від того, скільки buildзалежностей залежить від Росії clean. Як було написано вище, cleanє бар'єром синхронізації, тоді всі buildросійські залежності працюють паралельно, потім buildзапускаються.


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

Якщо повернути потік глотків, ви можете використовувати then()метод для додавання зворотного дзвінка. Крім того, ви можете використовувати рідний Node Promiseдля створення власних обіцянок. Тут я використовую Promise.all()один зворотний виклик, який спрацьовує, коли всі обіцянки вирішаться.


-8

Спробуйте цей злом :-) Gulp v3.x Hack for Async bug

Я спробував усі "офіційні" способи в "Редмі", вони не працювали для мене, але це було. Ви також можете оновити до 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.