Comment exécuter les tâches Gulp séquentiellement l'une après l'autre

398

dans l'extrait de code comme ceci:

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"

Dans la developtâche, je veux exécuter cleanet après avoir terminé, exécuter coffeeet quand cela est fait, exécuter autre chose. Mais je ne peux pas comprendre cela. Cette pièce ne fonctionne pas. S'il vous plaît donnez votre avis.

iLemming
la source
10
Le module run-sequence npm corrige ce problème maintenant - toutes les autres réponses ne sont plus pertinentes - voir la réponse d'OverZealous ci
danday74
1
Gulp 4.0 prend en charge nativement l'exécution des tâches en séquence, ce qui le rend run-sequenceobsolète - voir la réponse de massanishi ci
Forivin
Gulp4 casse plus de choses qu'il n'en résout, semble-t-il. Après quelques heures de lutte avec elle, je reviens au 3.9.1. Je me rends compte que les versions majeures peuvent / briseront la rétrocompatibilité mais avec des messages d'erreur cryptiques et inutiles, je dis non merci. la v4 n'est pas prête.
Sam Thiru

Réponses:

121

Ce n'est pas encore une version officielle, mais la prochaine version de Gulp 4.0 vous permet de faire facilement des tâches synchrones avec gulp.series. Vous pouvez simplement le faire comme ceci:

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

J'ai trouvé un bon article de blog présentant la mise à niveau et l'utilisation de ces fonctionnalités intéressantes: migration vers gulp 4 par exemple

massanishi
la source
3
Méthode de choix pour tous les nouveaux arrivants. Ils devraient vraiment commencer par gulp 4, en sautant tous les 3. * tracas et large éventail d'anti-modèles.
metalim
187
c'est 2017 et ils ne l'ont toujours pas présenté. Génial.
Tomasz Mularczyk
14
Je ne vois pas le point, vraiment. Si vous avez absolument besoin que A ne s'exécute qu'après l'exécution de B, alors A dépend de B. Pourquoi ne pouvez-vous pas simplement spécifier que B est une dépendance de A? gulp.task('coffee', ['clean'], function(){...}); gulp.task('develop', ['coffee']);
musicin3d
3
@ musicin3d Ce que vous dites fonctionne, mais vous associez nécessairement une tâche à la précédente. Par exemple, je veux pouvoir construire sans avoir à pelucher avant. C'est une meilleure solution pour avoir des tâches indépendantes et décider de l'ordre d'exécution avec un outil externe.
AxeEffect
11
c'est 2018 et ils l'ont finalement présenté. Génial.
dargmuesli
411

Par défaut, gulp exécute les tâches simultanément, sauf si elles ont des dépendances explicites. Ce n'est pas très utile pour des tâches comme clean, où vous ne voulez pas dépendre, mais vous en avez besoin pour s'exécuter avant tout le reste.

J'ai écrit le run-sequenceplugin spécifiquement pour résoudre ce problème avec gulp. Après l'avoir installé, utilisez-le comme ceci:

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

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

Vous pouvez lire les instructions complètes sur le paquet README - il prend également en charge l'exécution simultanée de certains ensembles de tâches.

Veuillez noter que cela sera (effectivement) corrigé dans la prochaine version majeure de gulp , car ils éliminent complètement l'ordre de dépendance automatique et fournissent des outils similaires àrun-sequence pour vous permettre de spécifier manuellement l'ordre d'exécution comme vous le souhaitez.

Cependant, c'est un changement majeur, il n'y a donc aucune raison d'attendre quand vous pouvez l'utiliser run-sequenceaujourd'hui.

OverZealous
la source
2
@OverZealous merci pour le plugin! Btw, le gulp-cleanplugin n'implémentait pas Streams 2, il a donc eu des problèmes en tant que dépendances. Cela a été corrigé à partir de la version 0.3.0, mon collègue a soumis un PR pour le convertir.
knownasilya
3
@Indolering La fonctionnalité de dépendance de tâche intégrée ne résout pas ce scénario. Les tâches dépendantes sont toujours exécutées: il n'y a aucun moyen intégré d'exécuter deux tâches d'affilée parfois , mais pas à chaque fois. run-sequencerésout un élément critique des fonctionnalités manquantes dans gulp.
OverZealous
2
De plus, les dépendances de tâches ne sont pas une solution complète. Disons que j'ai deux tâches gulp qui exécutent indépendamment des tests à l'aide de la base de données. Aucun ne dépend de l'autre, mais je ne veux pas que l'un d'eux s'exécute en même temps car ils ont tous deux besoin d'utiliser la base de données.
peterjwest
3
Module incroyable - Voici une excellente explication de la raison pour laquelle il est nécessaire - blog.mdnbar.com/gulp-for-simple-build-proccess - Cela devrait être la réponse acceptée
danday74
5
Je suis reconnaissant que vous ayez trouvé une solution à cela, mais il est absurde que nous ayons besoin d'outils supplémentaires pour obtenir une exécution séquentielle. L'exécution séquentielle devrait être la valeur par défaut, ou du moins facile à faire. Les Makefiles ont une exécution séquentielle depuis 40 ans. L'écosystème JS me rend malade. 73 mégaoctets de node_modules juste pour compiler un projet passe-partout sans aucune fonctionnalité, et cela n'inclut toujours pas la capacité d'exécution séquentielle. Les systèmes d'exploitation s'intègrent dans un espace plus petit et ils ont des noyaux et des pilotes FFS.
joonas.fi
371

La seule bonne solution à ce problème peut être trouvée dans la documentation de gulp qui peut être trouvée ici

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']);
Mathieu Borderé
la source
6
Pour une raison quelconque, ReferenceError: err is not definedj'essaie d'exécuter cela sur une gulp-compasstâche, est-ce que je manque quelque chose?
waffl
11
@waffl cet exemple utilise un rappel, ce qui n'est pas le seul moyen de le faire. Selon les documents , vous pouvez également "renvoyer une promesse ou un flux que le moteur doit attendre pour résoudre ou se terminer respectivement". Donc, si vous renvoyez un flux dans la tâche un, par exemple return gulp.src('app/**/*.js').pipe(concat(app.js)).pipe(gulp.dest('app/scripts');, la clé est d'identifier la tâche un comme dépendante lors de la définition de la tâche deux: la gulp.task('two', ['one'], function() {... tâche deux attendra maintenant la fin de la tâche un avant de s'exécuter.
esvendsen
24
Cela crée un couplage étroit entre «un» et «deux». Et si vous voulez exécuter «deux» sans exécuter «un»
wilk
5
vous définissez une nouvelle tâche sans la dépendance?
Mathieu Borderé
8
Le fait de devoir exécuter deux tâches séquentiellement implique un couplage étroit.
Ringo
56

J'ai généré une application node / gulp en utilisant le générateur Yeoman generator -gulp-webapp . Il a géré le "casse-tête propre" de cette façon (se traduisant par les tâches d'origine mentionnées dans la question):

gulp.task('develop', ['clean'], function () {
  gulp.start('coffee');
});
Steve
la source
2
C'était exactement (et très simple) dont j'avais besoin. Il aborde le scénario où j'ai besoin de faire quelque chose comme un nettoyage comme dépendance précédente pour créer des dépendances sans définir le nettoyage comme une dépendance pour ces tâches. PS: informations sur le bit gulp.start () - émetteur de
Jaans
Ça a du sens; un rappel une fois la tâche principale (et ses tâches dépendantes) terminée. Merci.
markau
4
Si quelqu'un se demande pourquoi il n'y a pas de documentation officielle gulp.start(), cette réponse d'un membre de gulp explique que: gulp.start is undocumented on purpose because it can lead to complicated build files and we don't want people using it(source: github.com/gulpjs/gulp/issues/426#issuecomment-41208007 )
thybzi
1
Dans ce cas, comment détecter que la coffeetâche est terminée? Si je ne le détecte pas, la developtâche se terminera plus tôt quecoffee
thybzi
déjà obtenu la réponse du run-sequencecode source: gulp.on('task_stop'). Voir ma réponse étendue pour plus de détails: stackoverflow.com/a/38818657/3027390
thybzi
32

run-sequence est le moyen le plus clair (au moins jusqu'à la sortie de Gulp 4.0)

Avec run-sequence , votre tâche ressemblera à ceci:

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

Mais si vous (pour une raison quelconque) préférez ne pas l'utiliser, la gulp.startméthode vous aidera :

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

Remarque: Si vous démarrez uniquement la tâche sans écouter le résultat, la developtâche se terminera plus tôt que coffee, et cela peut prêter à confusion.

Vous pouvez également supprimer l'écouteur d'événements lorsqu'il n'est pas nécessaire

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

Considérez qu'il y a aussi un task_errévénement que vous voudrez peut-être écouter. task_stopest déclenché en cas de réussite, tandis que task_errs'affiche en cas d'erreur.

Vous pouvez également vous demander pourquoi il n'y a pas de documentation officielle pour gulp.start(). Cette réponse d'un membre de gulp explique les choses:

gulp.start n'est pas documenté exprès car cela peut conduire à des fichiers de construction compliqués et nous ne voulons pas que les gens l'utilisent

(source: https://github.com/gulpjs/gulp/issues/426#issuecomment-41208007 )

thybzi
la source
deux cafés à cet homme! la solution avec la suppression de l'auditeur fonctionne parfaitement!
daniel.bavrin
C'est vraiment la réponse, ou tout simplement, "gulp 4". La séquence d'exécution est robuste.
LAdams87
26

Selon les documents Gulp:

Vos tâches s'exécutent-elles avant la fin des dépendances? Assurez-vous que vos tâches de dépendance utilisent correctement les astuces d'exécution asynchrone: prenez un rappel ou renvoyez une promesse ou un flux d'événements.

Pour exécuter votre séquence de tâches de manière synchrone:

  1. Renvoyez le flux d'événements (par exemple gulp.src) gulp.taskpour informer la tâche de la fin du flux.
  2. Déclarez les dépendances de tâche dans le deuxième argument de gulp.task.

Voir le code révisé:

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
la source
4
CECI devrait être la bonne réponse! La tâche de retour a fait l'affaire! Merci mec.
Dzoukr
10

J'avais exactement le même problème et la solution s'est avérée assez facile pour moi. Modifiez simplement votre code comme suit et cela devrait fonctionner. REMARQUE: le retour avant gulp.src a fait toute la différence pour moi.

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
la source
Merci pour la note sur le retour! Je devenais fou en essayant de comprendre pourquoi gulp était src-ing des tâches hors service.
Andrew F
Cela devrait être la bonne réponse pour éviter un couplage étroit entre les tâches. Fonctionne bien. gulp.series disponible dans 4.0 est probablement la meilleure réponse, mais à ce jour, 4.0 n'est pas disponible.
wilk
gulp develop coulera propre ou le café en premier
scape
8

essayé toutes les solutions proposées, tous semblent avoir leurs propres problèmes.

Si vous regardez réellement la source d'Orchestrator, en particulier l' .start()implémentation, vous verrez que si le dernier paramètre est une fonction, il la traitera comme un rappel.

J'ai écrit cet extrait pour mes propres tâches:

  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" ));
Assaf Moldavsky
la source
4

Je cherchais cette réponse depuis un moment. Maintenant, je l'ai dans la documentation officielle de Gulp.

Si vous souhaitez effectuer une tâche gulp lorsque la dernière est terminée, vous devez renvoyer un flux:

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

Lucho Suárez
la source
3

Faites simplement coffeedépendre cleanet developdépendez de coffee:

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

L'envoi est désormais en série: cleancoffeedevelop. Notez que cleanla mise en œuvre etcoffee l'implémentation doivent accepter un rappel, "afin que le moteur sache quand cela sera fait" :

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

En conclusion, voici un modèle de gulp simple pour une cleandépendance de construction synchrone suivie par des dépendances de construction asynchrones :

//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 est assez intelligent pour fonctionner cleanexactement une fois par build, quel que soit le nombre de builddépendances dont dépend clean. Comme écrit ci-dessus, cleanest une barrière de synchronisation, alors toutes buildles dépendances de s'exécutent en parallèle, puisbuild s'exécute.

akarve
la source
3

Pour moi, il n'exécutait pas la tâche minify après la concaténation car il attend une entrée concaténée et il n'a pas été généré quelques fois.

J'ai essayé d'ajouter à une tâche par défaut dans l'ordre d'exécution et cela n'a pas fonctionné. Cela a fonctionné après avoir ajouté juste un returnpour chaque tâche et obtenu la minification à l'intérieur gulp.start()comme ci-dessous.

/**
* 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']); 

Source http://schickling.me/synchronous-tasks-gulp/

devo
la source
2

Gulp et Node utilisent des promesses .

Vous pouvez donc faire ceci:

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

Si vous renvoyez le flux gulp, vous pouvez utiliser la then()méthode pour ajouter un rappel. Alternativement, vous pouvez utiliser le Node natif Promisepour créer vos propres promesses. Ici, j'ai l'habitude Promise.all()d'avoir un rappel qui se déclenche lorsque toutes les promesses sont résolues.

Peter J. Hart
la source
-8

Essayez ce hack :-) Gulp v3.x Hack for Async bug

J'ai essayé toutes les méthodes "officielles" du fichier Lisezmoi, elles ne fonctionnaient pas pour moi, mais cela a fonctionné. Vous pouvez également passer à gulp 4.x mais je vous le déconseille fortement, cela casse tellement de choses. Vous pourriez utiliser une vraie promesse js, mais bon, c'est rapide, sale, simple :-) Essentiellement, vous utilisez:

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

Consultez le post!

Will Bittner
la source
Il existe de meilleures façons de résoudre le problème, l'utilisation de setTimeout n'est pas appropriée. Vous ne pouviez pas savoir exactement combien de temps une tâche prendra pour terminer.
Reginaldo Camargo Ribeiro
Vous devriez vraiment réfléchir avant d'écrire un code pff
DDD