Como executar tarefas Gulp sequencialmente, uma após a outra

398

no trecho como este:

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"

Na developtarefa que quero executar, cleane depois que estiver pronto, execute coffeee, quando terminar, execute outra coisa. Mas não consigo descobrir isso. Esta peça não funciona. Por favor informar.

iLemming
fonte
10
O run-seqüência npm módulo corrige esse problema agora - todas as outras respostas são agora irrelevantes - veja a resposta de excesso de zelo abaixo
danday74
11
O Gulp 4.0 suporta nativamente tarefas em execução em sequência, tornando run-sequenceobsoletas - veja a resposta de massanishi abaixo
Forivin
Parece que o Gulp4 quebra mais coisas do que conserta. Depois de lutar por algumas horas, volto à 3.9.1. Sei que as principais versões podem / irão quebrar o backcompat, mas com mensagens de erro enigmáticas e inúteis, digo não, obrigado. v4 não está pronto.
precisa

Respostas:

121

Ainda não é um lançamento oficial, mas o Gulp 4.0 a seguir permite executar tarefas síncronas com facilidade com o gulp.series. Você pode simplesmente fazer assim:

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

Encontrei um bom post sobre como atualizar e fazer uso desses recursos legais: migrando para o gulp 4 por exemplo

massanishi
fonte
3
Método de escolha para todos os recém-chegados. Eles devem realmente começar com o gole 4, ignorando todos os problemas e a ampla gama de antipadrões.
metalim
187
seu 2017 e eles ainda não o introduziram. Ótimo.
Tomasz Mularczyk
14
Na verdade, não vejo o ponto. Se você absolutamente precisa de A para executar apenas após B, então A depende de B. Por que você não pode simplesmente especificar que B é uma dependência de A? gulp.task('coffee', ['clean'], function(){...}); gulp.task('develop', ['coffee']);
musicin3d
3
@ musicin3d O que você diz funciona, mas você está acoplando uma tarefa necessariamente à anterior. Por exemplo, eu quero poder construir sem ter que ficar sem fiapos antes. É uma solução melhor ter tarefas independentes e decidir a ordem de execução com uma ferramenta externa.
AxeEffect
11
seu 2018 e eles finalmente o introduziram. Ótimo.
dargmuesli
411

Por padrão, o gulp executa tarefas simultaneamente, a menos que tenham dependências explícitas. Isso não é muito útil para tarefas como clean, nas quais você não deseja depender, mas precisa que elas sejam executadas antes de tudo.

Eu escrevi o run-sequenceplugin especificamente para corrigir esse problema com o gulp. Depois de instalá-lo, use-o assim:

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

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

Você pode ler as instruções completas no pacote README - ele também suporta a execução de alguns conjuntos de tarefas simultaneamente.

Observe que isso será (efetivamente) corrigido na próxima versão principal do gulp , pois eles eliminam completamente a ordem automática de dependência e fornecem ferramentas semelhantes arun-sequence para permitir que você especifique manualmente a ordem de execução como desejar.

No entanto, essa é uma grande mudança, por isso não há razão para esperar quando você pode usar run-sequencehoje.

OverZealous
fonte
2
@OverZealous obrigado pelo plugin! Aliás, o gulp-cleanplug - in não estava implementando o Streams 2, portanto, havia problemas sendo executados como dependências. Isso foi corrigido na versão 0.3.0, meu colega de trabalho enviou um PR para convertê-lo.
knownasilya
3
@Indolering A funcionalidade interna de dependência de tarefas não resolve esse cenário. As tarefas dependentes são sempre executadas: não existe uma maneira integrada de executar duas tarefas seguidas algumas vezes, mas nem sempre . run-sequenceresolve uma parte crítica da funcionalidade ausente no gulp.
excesso de zelo
2
Além disso, as dependências de tarefas não são uma solução completa. Digamos que eu tenha duas tarefas gulp que executam testes independentemente usando o banco de dados. Nenhum deles depende do outro, mas não quero que nenhum deles seja executado ao mesmo tempo, porque ambos precisam usar o banco de dados.
Peterjwest
3
Módulo incrível - Heres uma ótima explicação de por que é necessário - blog.mdnbar.com/gulp-for-simple-build-proccess - Essa deve ser a resposta aceita
danday74
5
Sou grato por você ter criado uma solução para isso, mas é absurdo que precisemos de ferramentas adicionais para obter execução sequencial. A execução sequencial deve ser o padrão, ou pelo menos fácil de fazer. Makefiles tiveram execução sequencial por 40 anos. O ecossistema JS me deixa doente. 73 megabytes de node_modules apenas para compilar um projeto padrão sem nenhum recurso, e isso ainda não inclui a capacidade de execução seqüencial. Os sistemas operacionais cabem em um espaço menor e possuem Kernels e drivers FFS.
91317 joonas.fi
371

A única boa solução para esse problema pode ser encontrada na documentação do gulp, que pode ser encontrada aqui

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é
fonte
6
Por algum motivo, estou ReferenceError: err is not definedtentando executar isso em uma gulp-compasstarefa, estou perdendo alguma coisa?
28815
11
@waffl neste exemplo usa um retorno de chamada, que não é a única maneira de fazer isso. De acordo com os documentos , você também pode "retornar uma promessa ou fluxo que o mecanismo deve esperar para resolver ou terminar respectivamente". Portanto, se você retornar um fluxo na tarefa 1, por exemplo return gulp.src('app/**/*.js').pipe(concat(app.js)).pipe(gulp.dest('app/scripts');, a chave é identificar a tarefa 1 como dependente ao definir a tarefa 2: a gulp.task('two', ['one'], function() {... tarefa 2 agora aguardará a conclusão da tarefa 1 antes da execução.
esvendsen 02/09/2015
24
Isso cria um forte acoplamento entre 'um' e 'dois'. E se você quiser executar 'two' sem executar 'one'
wilk 02/12/2015
5
você define uma nova tarefa sem a dependência?
Mathieu Borderé
8
A necessidade de duas tarefas para executar sequencialmente implica um acoplamento rígido.
Ringo
56

Eu criei um aplicativo node / gulp usando o gerador Yeoman do generator-gulp-webapp . Ele lidou com o "enigma limpo" dessa maneira (traduzindo para as tarefas originais mencionadas na pergunta):

gulp.task('develop', ['clean'], function () {
  gulp.start('coffee');
});
Steve
fonte
2
Era exatamente isso (e muito simples) que eu precisava. Ele aborda o cenário em que eu preciso fazer algo como uma dependência limpa como uma precedente para criar dependências sem definir a limpeza como uma dependência para essas tarefas. PS: Informações sobre o bit gulp.start () - caveat emptor: github.com/gulpjs/gulp/issues/426
Jaans
Isso faz sentido; um retorno de chamada após a conclusão da tarefa principal (e das tarefas dependentes). Obrigado.
Markau # 21/16
4
Se alguém está se perguntando por que não há nenhuma documentação oficial para gulp.start(), esta resposta do membro gole explica que: gulp.start is undocumented on purpose because it can lead to complicated build files and we don't want people using it(fonte: github.com/gulpjs/gulp/issues/426#issuecomment-41208007 )
thybzi
11
Nesse caso, como posso detectar que a coffeetarefa foi concluída? Se eu não detectá-lo, developtarefa vai terminar mais cedo do quecoffee
thybzi
já tem a resposta de run-sequencecódigo fonte: gulp.on('task_stop'). Veja minha resposta estendida para obter detalhes: stackoverflow.com/a/38818657/3027390
thybzi
32

A sequência de execução é a maneira mais clara (pelo menos até o Gulp 4.0 ser lançado)

Com a sequência de execução , sua tarefa ficará assim:

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

Mas se você (por algum motivo) preferir não usá-lo, o gulp.startmétodo ajudará :

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

Nota: Se você iniciar a tarefa apenas sem ouvir o resultado, a developtarefa será concluída mais cedo coffeee isso pode ser confuso.

Você também pode remover o ouvinte de eventos quando não for necessário

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

Considere que também há um task_errevento que você pode querer ouvir. task_stopé acionado na conclusão bem-sucedida, enquanto task_erraparece quando há algum erro.

Você também pode se perguntar por que não há documentação oficial para gulp.start(). Esta resposta do membro gulp explica o seguinte:

gulp.start não é documentado de propósito porque pode levar a arquivos de compilação complicados e não queremos que as pessoas o usem

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

thybzi
fonte
dois cafés para esse homem! a solução com a remoção do ouvinte funciona perfeitamente!
Daniel.bavrin
Esta é realmente a resposta, ou apenas "gole 4". A sequência de execução é robusta.
LAdams87
26

De acordo com os documentos Gulp:

Suas tarefas estão sendo executadas antes que as dependências sejam concluídas? Verifique se as tarefas de dependência estão usando corretamente as dicas de execução assíncrona: receba um retorno de chamada ou retorne uma promessa ou fluxo de eventos.

Para executar sua sequência de tarefas de forma síncrona:

  1. Retorne o fluxo de eventos (por exemplo gulp.src) para gulp.taskpara informar a tarefa de quando o fluxo termina.
  2. Declare dependências de tarefas no segundo argumento de gulp.task.

Veja o código revisado:

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
fonte
4
ESTA deve ser a resposta correta! A tarefa de retorno fez o truque! Obrigado cara.
Dzoukr
10

Eu estava tendo exatamente o mesmo problema e a solução acabou sendo muito fácil para mim. Altere basicamente o seu código para o seguinte e ele deve funcionar. NOTA: o retorno antes do gulp.src fez toda a diferença para mim.

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
fonte
Obrigado pela observação ao retornar! Estava enlouquecendo tentando descobrir por que o gulp estava executando tarefas fora de ordem.
Andrew F
Essa deve ser a resposta correta para evitar um acoplamento rígido entre as tarefas. Funciona bem. O gulp.series disponível no 4.0 é provavelmente a melhor resposta, mas, atualmente, o 4.0 não está disponível.
Wilk
Gulp desenvolver será executado limpo ou café primeira
scape
8

tentei todas as soluções propostas, todas parecem ter problemas próprios.

Se você realmente procurar a fonte do Orchestrator, particularmente a .start()implementação, verá que, se o último parâmetro for uma função, ele será tratado como um retorno de chamada.

Eu escrevi este trecho para minhas próprias tarefas:

  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
fonte
4

Eu estava procurando por essa resposta por um tempo. Agora eu peguei isso na documentação oficial do gulp.

Se você deseja executar uma tarefa gulp quando a última estiver concluída, você deve retornar um fluxo:

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
fonte
3

Basta fazer coffeedepender cleane developdepender de coffee:

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

A expedição agora é serial: cleancoffeedevelop. Observe que cleana implementação e coffeea implementação devem aceitar um retorno de chamada "para que o mecanismo saiba quando será feito" :

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

Em conclusão, abaixo está um padrão simples de gulp para um síncrono cleanseguido por dependências de construção assíncronas :

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

O Gulp é inteligente o suficiente para executar cleanexatamente uma vez por build, independentemente de quantas builddependências dependam clean. Como escrito acima, cleané uma barreira de sincronização, então todas buildas dependências são executadas em paralelo e depois buildexecutadas.

akarve
fonte
3

Para mim, não estava executando a tarefa minify após a concatenação, pois espera uma entrada concatenada e não foi gerada algumas vezes.

Tentei adicionar a uma tarefa padrão em ordem de execução e ela não funcionou. Funcionou depois de adicionar apenas um returnpara cada tarefa e obter a minificação dentro, gulp.start()como abaixo.

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

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

devo
fonte
2

Gulp e Node usam promessas .

Então você pode fazer isso:

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

Se você retornar o fluxo gulp, poderá usar o then()método para adicionar um retorno de chamada. Como alternativa, você pode usar o nativo do Node Promisepara criar suas próprias promessas. Aqui eu uso Promise.all()para ter um retorno de chamada que é acionado quando todas as promessas são resolvidas.

Peter J. Hart
fonte
-8

Tente este hack :-) Gulp v3.x Hack for Async bug

Eu tentei de todas as maneiras "oficiais" no Leiame, elas não funcionaram para mim, mas funcionou. Você também pode atualizar para o gulp 4.x, mas eu recomendo que não, pois quebra muitas coisas. Você pode usar uma promessa js real, mas, ei, isso é rápido, sujo, simples :-) Basicamente, você usa:

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

Confira o post!

Will Bittner
fonte
Existem maneiras melhores de resolver o problema, o uso de setTimeout não é apropriado. Você não sabia exatamente quanto tempo uma tarefa levaria para terminar.
Reginaldo Camargo Ribeiro
Você realmente deve pensar antes de escrever qualquer código pff
DDD