Cómo ejecutar tareas Gulp secuencialmente una tras otra

398

en el fragmento así:

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"

En la developtarea que quiero ejecutar cleany después de que esté hecho, ejecute coffeey cuando haya terminado, ejecute otra cosa. Pero no puedo entender eso. Esta pieza no funciona. Por favor avise.

iLemming
fuente
10
El módulo npm de secuencia de ejecución soluciona este problema ahora; todas las demás respuestas ahora son irrelevantes; consulte la respuesta de OverZealous a continuación
danday74
1
Gulp 4.0 admite de forma nativa las tareas en ejecución en secuencia, quedando run-sequenceobsoleto - vea la respuesta de massanishi a continuación
Forivin
Parecería que Gulp4 rompe más cosas de las que arregla. Después de luchar con él durante unas horas, vuelvo a 3.9.1. Me doy cuenta de que las versiones principales pueden / romperán el backcompat pero con mensajes de error crípticos e inútiles, digo que no, gracias. v4 no está listo.
Sábado Thiru

Respuestas:

121

Todavía no es un lanzamiento oficial, pero el próximo Gulp 4.0 le permite realizar fácilmente tareas sincrónicas con gulp.series. Simplemente puedes hacerlo así:

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

Encontré una buena publicación de blog que presenta cómo actualizar y utilizar esas características interesantes: migrar a Gulp 4 por ejemplo

Massanishi
fuente
3
Método de elección para todos los recién llegados. Realmente deberían comenzar con el trago 4, omitiendo los 3. * problemas y una amplia gama de antipatrones.
metalim
187
es 2017 y todavía no lo presentaron. Excelente.
Tomasz Mularczyk
14
No veo el punto, de verdad. Si necesita absolutamente que A se ejecute solo después de que B se ejecute, entonces A depende de B. ¿Por qué no puede simplemente especificar que B es una dependencia de A? gulp.task('coffee', ['clean'], function(){...}); gulp.task('develop', ['coffee']);
musicin3d
3
@ musicin3d Lo que dices funciona, pero estás acoplando una tarea necesariamente a la anterior. Por ejemplo, quiero poder construir sin tener que usar pelusas antes. Es una mejor solución tener tareas independientes y decidir el orden de ejecución con una herramienta externa.
AxeEffect
11
es 2018 y finalmente lo presentaron. Excelente.
dargmuesli
411

Por defecto, gulp ejecuta tareas simultáneamente, a menos que tengan dependencias explícitas. Esto no es muy útil para tareas como clean, donde no desea depender, pero necesita que se ejecuten antes que todo lo demás.

Escribí el run-sequencecomplemento específicamente para solucionar este problema con trago. Después de instalarlo, úsalo así:

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

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

Puede leer las instrucciones completas en el paquete README: también admite ejecutar algunos conjuntos de tareas simultáneamente.

Tenga en cuenta que esto se solucionará (de manera efectiva) en la próxima versión principal de Gulp , ya que están eliminando por completo el orden de dependencia automático y proporcionando herramientas similares run-sequencepara permitirle especificar manualmente el orden de ejecución como lo desee.

Sin embargo, ese es un cambio importante, por lo que no hay razón para esperar cuando puede usarlo run-sequencehoy.

Demasiado entusiasta
fuente
2
@OverZealous gracias por el complemento! Por cierto, el gulp-cleancomplemento no estaba implementando Streams 2, por lo que tenía problemas para ejecutarse como dependencias. Esto se ha solucionado a partir de la versión de lanzamiento 0.3.0, mi compañero de trabajo envió un RP para convertirlo.
conocidoasilya
3
@Indolering La funcionalidad de dependencia de tareas incorporada no resuelve este escenario. Tareas dependientes se ejecutan siempre: no hay Está construido en forma de ejecutar dos tareas consecutivas alguna parte del tiempo, pero no todos los tiempo. run-sequenceresuelve una pieza crítica de funcionalidad faltante en trago.
OverZealous
2
Además, las dependencias de tareas no son una solución completa. Digamos que tengo dos tareas rápidas que ejecutan pruebas de forma independiente utilizando la base de datos. Ninguno de los dos depende del otro, pero no quiero que ninguno de ellos se ejecute al mismo tiempo porque ambos necesitan usar la base de datos.
peterjwest
3
Increíble módulo - Heres una gran explicación de por qué es necesario - blog.mdnbar.com/gulp-for-simple-build-proccess - Esta debe ser la respuesta aceptada
danday74
55
Estoy agradecido de que haya encontrado una solución para esto, pero es absurdo que necesitemos herramientas adicionales para obtener una ejecución secuencial. La ejecución secuencial debería ser la predeterminada, o al menos una tarea fácil. Los Makefiles han tenido una ejecución secuencial durante 40 años. El ecosistema JS me enferma. 73 megabytes de node_modules solo para compilar un proyecto repetitivo sin ninguna característica, y eso todavía no incluye la capacidad de ejecución secuencial. Los sistemas operativos caben en un espacio más pequeño y tienen Kernels y controladores FFS.
joonas.fi
371

La única buena solución a este problema se puede encontrar en la documentación de Gulp que se puede encontrar aquí.

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é
fuente
66
Por alguna razón estoy ReferenceError: err is not definedtratando de ejecutar esto en una gulp-compasstarea, ¿me estoy perdiendo algo?
waffl
11
@waffl este ejemplo utiliza una devolución de llamada, que no es la única forma de hacerlo. Según los documentos , también puede "devolver una promesa o una transmisión que el motor debe esperar para resolver o finalizar, respectivamente". Entonces, si devuelve una secuencia en la tarea uno, por ejemplo return gulp.src('app/**/*.js').pipe(concat(app.js)).pipe(gulp.dest('app/scripts');, la clave es identificar la tarea uno como dependiente al definir la tarea dos: la gulp.task('two', ['one'], function() {... tarea dos ahora esperará a que termine la tarea uno antes de ejecutarse.
esvendsen
24
Esto crea un acoplamiento estrecho entre 'uno' y 'dos'. ¿Qué
sucede
55
define una nueva tarea sin la dependencia?
Mathieu Borderé
8
La necesidad de ejecutar dos tareas secuencialmente implica un acoplamiento estrecho.
Ringo
56

Genere una aplicación de nodo / trago usando el generador Yeoman de generator -gulp-webapp . Manejó el "acertijo limpio" de esta manera (traduciéndose a las tareas originales mencionadas en la pregunta):

gulp.task('develop', ['clean'], function () {
  gulp.start('coffee');
});
Steve
fuente
2
Esto era exactamente (y muy simple) lo que necesitaba. Aborda el escenario en el que necesito hacer algo como limpiar como una dependencia precedente para construir dependencias sin configurar clean como una dependencia para esas tareas. PS: Información acerca de la gulp.start () bit - Cuidado con este hotel: github.com/gulpjs/gulp/issues/426
Jaans
Eso tiene sentido; una devolución de llamada después de que se haya completado la tarea principal (y sus tareas dependientes). Gracias.
markau
44
Si alguien se pregunta por qué no hay documentación oficial gulp.start(), esta respuesta del miembro de Gulp explica que: gulp.start is undocumented on purpose because it can lead to complicated build files and we don't want people using it(fuente: github.com/gulpjs/gulp/issues/426#issuecomment-41208007 )
thybzi
1
En este caso, ¿cómo puedo detectar que la coffeetarea ha finalizado? Si no lo detecto, la developtarea finalizará antes decoffee
thybzi
ya tiene la respuesta del run-sequencecódigo fuente: gulp.on('task_stop'). Vea mi respuesta extendida para más detalles: stackoverflow.com/a/38818657/3027390
thybzi
32

La secuencia de ejecución es la forma más clara (al menos hasta que se publique Gulp 4.0)

Con la secuencia de ejecución , su tarea se verá así:

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

Pero si (por alguna razón) prefiere no usarlo, el gulp.startmétodo ayudará :

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

Nota: Si solo inicia la tarea sin escuchar el resultado, la developtarea finalizará antes coffeey eso puede ser confuso.

También puede eliminar el detector de eventos cuando no sea necesario

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 también hay un task_errevento que tal vez quiera escuchar. task_stopse activa al finalizar con éxito, mientras task_erraparece cuando hay algún error.

También puede preguntarse por qué no hay documentación oficial para gulp.start(). Esta respuesta del miembro de Gulp explica las cosas:

gulp.start no está documentado a propósito porque puede generar archivos de compilación complicados y no queremos que la gente lo use

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

thybzi
fuente
¡Dos cafés para este hombre! ¡La solución para eliminar el oyente funciona perfectamente!
daniel.bavrin
Esta es realmente la respuesta, o simplemente, "tragar 4". La secuencia de ejecución es robusta.
LAdams87
26

De acuerdo con los documentos de Gulp:

¿Sus tareas se ejecutan antes de que se completen las dependencias? Asegúrese de que sus tareas de dependencia estén utilizando correctamente las sugerencias de ejecución asíncrona: reciba una devolución de llamada o devuelva una secuencia de promesa o evento.

Para ejecutar su secuencia de tareas sincrónicamente:

  1. Devuelva la secuencia del evento (por ejemplo gulp.src) a gulp.taskpara informar a la tarea cuándo finaliza la secuencia.
  2. Declarar dependencias de tareas en el segundo argumento de gulp.task.

Ver el 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
fuente
44
¡ESTA debería ser la respuesta correcta! Volver tarea hizo el truco! Gracias hombre.
Dzoukr
10

Estaba teniendo exactamente el mismo problema y la solución resultó ser bastante fácil para mí. Básicamente cambie su código a lo siguiente y debería funcionar. NOTA: la devolución antes de gulp.src marcó la diferencia para mí.

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
fuente
Gracias por la nota al regresar! Se estaba volviendo loco tratando de entender por qué tragar salía a la basura las tareas.
Andrew F
Esta debería ser la respuesta correcta para evitar un acoplamiento estrecho entre tareas. Funciona bien. gulp.series disponible en 4.0 es probablemente la mejor respuesta, pero a partir de hoy, 4.0 no está disponible.
wilk
el trago se desarrollará limpio o el café primero
scape
8

Probé todas las soluciones propuestas, todas parecen tener problemas propios.

Si realmente busca en la fuente del orquestador, particularmente en la .start()implementación, verá que si el último parámetro es una función, lo tratará como una devolución de llamada.

Escribí este fragmento para mis propias tareas:

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

Estuve buscando esta respuesta por un tiempo. Ahora lo tengo en la documentación oficial de Gulp.

Si desea realizar una tarea de trago cuando se complete la última, debe devolver una secuencia:

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

Simplemente haga coffeedepender cleany developdependa de coffee:

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

El envío ahora es serial: cleancoffeedevelop. Tenga en cuenta que cleanla implementación y coffeela implementación deben aceptar una devolución de llamada, "para que el motor sepa cuándo se realizará" :

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

En conclusión, a continuación se muestra un patrón de trago simple para una cleandependencia de compilación síncrona seguida de asíncrona :

//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 es lo suficientemente inteligente como para ejecutarse cleanexactamente una vez por cada uno build, sin importar de cuántas builddependencias dependan clean. Como se escribió anteriormente, cleanes una barrera de sincronización, entonces todas buildlas dependencias se ejecutan en paralelo, luego se buildejecuta.

akarve
fuente
3

Para mí, no estaba ejecutando la tarea minify después de la concatenación, ya que espera una entrada concatenada y no se generó algunas veces.

Intenté agregar a una tarea predeterminada en orden de ejecución y no funcionó. Funcionó después de agregar solo un returnpara cada tarea y obtener la minificación dentro gulp.start()como se muestra a continuación.

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

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

devo
fuente
2

Gulp y Node usan promesas .

Entonces puedes hacer esto:

// ... 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 devuelve la secuencia de trago, puede usar el then()método para agregar una devolución de llamada. Alternativamente, puede usar el nativo de Node Promisepara crear sus propias promesas. Aquí solía Promise.all()tener una devolución de llamada que se activa cuando se resuelven todas las promesas.

Peter J. Hart
fuente
-8

Prueba este truco :-) Gulp v3.x Hack para error asíncrono

Probé todas las formas "oficiales" en el archivo Léame, no funcionaron para mí, pero esto sí. También puede actualizar a Gulp 4.x, pero le recomiendo que no lo haga, rompe muchas cosas. Podría usar una promesa js real, pero bueno, esto es rápido, sucio, simple :-) Esencialmente usa:

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

¡Mira la publicación!

Will Bittner
fuente
Hay mejores formas de resolver el problema, usando setTimeout no es apropiado. No podría saber exactamente cuánto tiempo llevará terminar una tarea.
Reginaldo Camargo Ribeiro
Realmente deberías pensar antes de escribir cualquier código pff
DDD