Jak kolejno uruchamiać zadania Gulp jeden po drugim

398

we fragmencie takim jak ten:

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"

W developzadaniu chcę uruchomić, cleana po jego zakończeniu uruchomić, coffeea kiedy to zrobisz, uruchom coś innego. Ale nie mogę tego rozgryźć. Ten kawałek nie działa. Proszę doradź.

iLemming
źródło
10
Moduł npm sekwencji uruchomieniowej rozwiązuje teraz ten problem - wszystkie inne odpowiedzi są teraz nieistotne - patrz odpowiedź
OverZealousa
1
Gulp 4.0 natywnie obsługuje uruchamianie zadań w sekwencji, czyniąc je run-sequenceprzestarzałymi - patrz odpowiedź
massanishi
Wydaje się, że Gulp4 łamie więcej rzeczy niż naprawia. Po kilku godzinach walki wracam do 3.9.1. Zdaję sobie sprawę, że główne wersje mogą / przełamią backcompat, ale z tajemniczymi i bezużytecznymi komunikatami o błędach, mówię, że nie, dziękuję. Wersja 4 nie jest gotowa.
Sat Thiru

Odpowiedzi:

121

To nie jest jeszcze oficjalna wersja, ale nadchodzący Gulp 4.0 pozwala łatwo wykonywać synchroniczne zadania z gulp.series. Możesz to po prostu zrobić w następujący sposób:

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

Znalazłem dobry post na blogu, w którym przedstawiłem, jak uaktualnić i wykorzystać te fajne funkcje: migracja do gulp 4 przez przykład

massanishi
źródło
3
Metoda wyboru dla wszystkich nowo przybyłych. Powinny zacząć od łyku 4, pomijając wszystkie 3. * kłopoty i szeroką gamę antypatternów.
metalim
187
jest rok 2017 i nadal go nie wprowadzili. Świetny.
Tomasz Mularczyk
14
Naprawdę nie widzę sensu. Jeśli absolutnie potrzebujesz, aby A działało dopiero po uruchomieniu B, to A zależy od B. Dlaczego nie możesz po prostu określić, że B jest zależnością A? gulp.task('coffee', ['clean'], function(){...}); gulp.task('develop', ['coffee']);
musicin3d
3
@ musicin3d To, co mówisz, działa, ale łączysz jedno zadanie koniecznie z poprzednim. Na przykład chcę być w stanie budować bez konieczności wcześniejszego strzępienia. Lepszym rozwiązaniem jest posiadanie niezależnych zadań i ustalanie kolejności wykonania za pomocą zewnętrznego narzędzia.
AxeEffect
11
jest rok 2018 i wreszcie go wprowadzili. Świetny.
dargmuesli
411

Domyślnie gulp uruchamia zadania jednocześnie, chyba że mają wyraźne zależności. Nie jest to zbyt przydatne w przypadku zadań takich clean, w których nie chcesz polegać, ale musisz je uruchomić przed wszystkim innym.

Napisałem ten run-sequenceplugin specjalnie, aby rozwiązać ten problem z haustem. Po zainstalowaniu użyj go w następujący sposób:

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

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

Możesz przeczytać pełne instrukcje na pakiecie README - obsługuje także uruchamianie niektórych zestawów zadań jednocześnie.

Uwaga: zostanie to (skutecznie) naprawione w następnej głównej wersji gulp , ponieważ całkowicie eliminują one automatyczne porządkowanie zależności i udostępniają narzędzia podobne run-sequencedo pozwalające na ręczne określenie kolejności uruchamiania według potrzeb .

Jest to jednak znacząca przełomowa zmiana, więc nie ma powodu, aby czekać, kiedy będzie można ją run-sequencedziś wykorzystać .

OverZealous
źródło
2
@OverZealous dzięki za wtyczkę! Poza tym gulp-cleanwtyczka nie implementowała strumieni 2, więc występowały problemy jako zależności. Zostało to naprawione od wersji 0.3.0, mój współpracownik przesłał PR, aby go przekonwertować.
knownasilya
3
@Indolering Wbudowana funkcja zależności zadań nie rozwiązuje tego scenariusza. Zadania zależne są zawsze uruchamiane: nie ma wbudowanego sposobu na uruchomienie dwóch zadań z rzędu przez pewien czas, ale nie za każdym razem. run-sequencerozwiązuje krytyczny brakujący element w gulp.
OverZealous,
2
Również zależności zadań nie są kompletnym rozwiązaniem. Powiedzmy, że mam dwa zadania przełknięcia, które niezależnie uruchamiają testy przy użyciu bazy danych. Żadne z nich nie jest zależne od drugiego, ale nie chcę, aby którekolwiek z nich działało w tym samym czasie, ponieważ oba muszą korzystać z bazy danych.
peterjwest
3
Niesamowity moduł - Oto świetne wyjaśnienie, dlaczego jest potrzebne - blog.mdnbar.com/gulp-for-simple-build-proccess - To powinna być zaakceptowana odpowiedź
danday74
5
Jestem wdzięczny za rozwiązanie tego problemu, ale absurdalne jest, że potrzebujemy dodatkowego oprzyrządowania, aby uzyskać sekwencyjne wykonanie. Sekwencyjne wykonywanie powinno być domyślne, a przynajmniej kurwa łatwe. Pliki makefile są wykonywane sekwencyjnie od 40 lat. Ekosystem JS robi mi się niedobrze. 73 megabajtów modułów node_modules tylko do skompilowania projektu typu „kocioł” bez żadnych funkcji, a to wciąż nie obejmuje możliwości sekwencyjnego wykonywania. Systemy operacyjne mieszczą się na mniejszej przestrzeni i mają FFS jądra i sterowników.
joonas.fi
371

Jedyne dobre rozwiązanie tego problemu można znaleźć w dokumentacji łyka, którą można znaleźć tutaj

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é
źródło
6
Z jakiegoś powodu ReferenceError: err is not definedpróbuję uruchomić to w gulp-compasszadaniu, czy coś mi brakuje?
waffl
11
@waffl w tym przykładzie używa wywołania zwrotnego, co nie jest jedynym sposobem, aby to zrobić. Zgodnie z dokumentami możesz także „zwrócić obietnicę lub strumień, że silnik powinien czekać, aż odpowiednio rozwiąże lub zakończy”. Jeśli więc zwrócisz strumień w pierwszym zadaniu, np return gulp.src('app/**/*.js').pipe(concat(app.js)).pipe(gulp.dest('app/scripts');. Kluczem jest zidentyfikowanie pierwszego zadania jako zależnego podczas definiowania drugiego zadania: gulp.task('two', ['one'], function() {... Zadanie drugie będzie teraz czekać na zakończenie zadania pierwszego przed uruchomieniem.
esvendsen
24
Tworzy to ścisłe połączenie między „jednym” a „dwoma”. Co jeśli chcesz uruchomić „dwa” bez uruchamiania „jednego”
wilk
5
definiujesz nowe zadanie bez zależności?
Mathieu Borderé
8
Potrzeba dwóch zadań do uruchomienia sekwencyjnego oznacza ścisłe połączenie.
Ringo,
56

Wygenerowałem aplikację node / gulp za pomocą generatora Yeul generatora gulp-webapp . W ten sposób poradzono sobie z „czystą zagadką” (przekładając się na oryginalne zadania wymienione w pytaniu):

gulp.task('develop', ['clean'], function () {
  gulp.start('coffee');
});
Steve
źródło
2
To było dokładnie (i bardzo proste) potrzebne. Odnosi się do scenariusza, w którym muszę zrobić coś czystego jako poprzednia zależność, aby budować zależności bez ustawiania czystości jako zależności od tych zadań. PS: Informacje o bicie gulp.start () - zastrzeżenie emptor: github.com/gulpjs/gulp/issues/426
Jaans
To ma sens; oddzwonienie po zakończeniu głównego zadania (i jego zadań zależnych). Dzięki.
markau 21.04.16
4
Jeśli ktoś zastanawia się, dlaczego nie ma oficjalnej dokumentacji gulp.start(), ta odpowiedź członka gulp wyjaśnia, że: gulp.start is undocumented on purpose because it can lead to complicated build files and we don't want people using it(źródło: github.com/gulpjs/gulp/issues/426#issuecomment-41208007 )
thybzi
1
W takim przypadku, jak mogę wykryć, że coffeezadanie zostało zakończone? Jeśli go nie developcoffee
wykryję
już dostałem odpowiedź z run-sequencekodem źródłowym: gulp.on('task_stop'). Zobacz moją rozszerzoną odpowiedź, aby uzyskać szczegółowe informacje: stackoverflow.com/a/38818657/3027390
thybzi
32

sekwencja uruchamiania jest najbardziej przejrzystym sposobem (przynajmniej do wydania Gulp 4.0)

W przypadku sekwencji uruchamiania twoje zadanie będzie wyglądać następująco:

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

Ale jeśli (z jakiegoś powodu) wolisz go nie używać, gulp.startmetoda pomoże :

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

Uwaga: jeśli rozpoczniesz zadanie tylko bez odsłuchiwania wyniku, developzadanie zakończy się wcześniej niż coffee, co może być mylące.

Możesz także usunąć detektor zdarzeń, gdy nie jest potrzebny

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

Zastanów się, czy jest też task_errwydarzenie, którego możesz chcieć słuchać. task_stopjest uruchamiany po pomyślnym zakończeniu, podczas gdytask_err pojawia się, gdy wystąpi jakiś błąd.

Możesz także zastanawiać się, dlaczego nie ma oficjalnej dokumentacji gulp.start(). Ta odpowiedź członka gulp wyjaśnia następujące rzeczy:

gulp.start jest celowo nieudokumentowane, ponieważ może prowadzić do skomplikowanych plików kompilacji i nie chcemy, aby ludzie go używali

(źródło: https://github.com/gulpjs/gulp/issues/426#issuecomment-41208007 )

thybzi
źródło
dwie kawy dla tego mężczyzny! rozwiązanie z usunięciem detektora działa idealnie!
daniel.bavrin
To naprawdę jest odpowiedź lub po prostu „gulp 4”. Sekwencja uruchamiania jest niezawodna.
LAdams87
26

Według dokumentów Gulpa:

Czy Twoje zadania działają przed ukończeniem zależności? Upewnij się, że zadania zależności działają poprawnie, korzystając ze wskazówek uruchamiania asynchronicznego: odbierz wywołanie zwrotne lub zwróć obietnicę lub strumień zdarzeń.

Aby uruchomić sekwencję zadań synchronicznie:

  1. Zwróć strumień zdarzeń (np. gulp.src), gulp.taskAby poinformować zadanie o zakończeniu strumienia.
  2. Zadeklaruj zależności zadań w drugim argumencie gulp.task.

Zobacz poprawiony kod:

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
źródło
4
TO powinna być poprawna odpowiedź! Zadanie powiodło się! Dzięki stary.
Dzoukr,
10

Miałem dokładnie ten sam problem i rozwiązanie okazało się dla mnie dość łatwe. Zasadniczo zmień kod na następujący i powinien on działać. UWAGA: powrót przed gulp.src zrobił dla mnie całą różnicę.

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
źródło
Dzięki za notatkę o powrocie! Oszalałam, próbując dowiedzieć się, dlaczego przełykanie zleceń nie działa.
Andrew F,
To powinna być poprawna odpowiedź, aby uniknąć ścisłego powiązania między zadaniami. Działa dobrze. gulp.series dostępny w 4.0 jest prawdopodobnie najlepszą odpowiedzią, ale na dzień dzisiejszy 4.0 nie jest dostępny.
wilk
łyk wywoła najpierw czysty lub kawę
zgłoś
8

wypróbowałem wszystkie proponowane rozwiązania, wszystkie wydają się mieć własne problemy.

Jeśli faktycznie zajrzysz do źródła Orchestratora, szczególnie do .start() implementacji, zobaczysz, że jeśli ostatni parametr jest funkcją, będzie traktował go jako wywołanie zwrotne.

Napisałem ten fragment do moich własnych zadań:

  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
źródło
4

Przez chwilę szukałem tej odpowiedzi. Teraz mam to w oficjalnej dokumentacji łyka.

Jeśli chcesz wykonać zadanie przełknięcia po zakończeniu ostatniego, musisz zwrócić strumień:

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
źródło
3

Po prostu coffeeuzależnij się cleani developpolegaj na coffee:

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

Wysyłka jest teraz seryjna: cleancoffeedevelop. Pamiętaj, że cleanimplementacja i coffeeimplementacja muszą akceptować wywołanie zwrotne, „aby silnik wiedział, kiedy to nastąpi” :

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

Podsumowując, poniżej znajduje się prosty wzorzec przełknięcia dla synchronicznych cleanzależności, po których następują asynchroniczne zależności kompilacji :

//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 jest wystarczająco inteligentny, aby uruchamiać cleandokładnie raz na build, bez względu na to, ile buildzależy od zależności clean. Jak napisano powyżej, cleanjest barierą synchronizacji, wtedy wszystkie buildzależności działają równolegle, a następnie builddziałają.

akarve
źródło
3

Dla mnie nie było uruchomione zadanie minify po konkatenacji, ponieważ oczekuje ono skonkatowanych danych wejściowych i nie zostało wygenerowane kilka razy.

Próbowałem dodać do domyślnego zadania w kolejności wykonania i nie zadziałało. Działa po dodaniu tylko returndla każdego zadania i umieszczeniu w środku minifikacji gulp.start()jak poniżej.

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

Źródło http://schickling.me/synchronous-tasks-gulp/

devo
źródło
2

Gulp i Node używają obietnic .

Możesz to zrobić:

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

Jeśli zwrócisz strumień gulp, możesz użyć then()metody, aby dodać wywołanie zwrotne. Alternatywnie możesz użyć natywnego węzła, Promiseaby stworzyć własne obietnice. Tutaj używam Promise.all()jednego oddzwaniania, które jest uruchamiane, gdy wszystkie obietnice się spełnią.

Peter J. Hart
źródło
-8

Spróbuj tego hacka :-) Błąd Gulp v3.x Hack for Async

Próbowałem wszystkich „oficjalnych” sposobów w Readme, nie działały one dla mnie, ale to zadziałało. Możesz także zaktualizować do wersji gulp 4.x, ale zdecydowanie nie polecam, ponieważ psuje tak wiele rzeczy. Możesz użyć prawdziwej obietnicy js, ale hej, to jest szybkie, brudne, proste :-) Zasadniczo używasz:

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

Sprawdź post!

Will Bittner
źródło
Są lepsze sposoby rozwiązania problemu, użycie setTimeout nie jest odpowiednie. Nie można dokładnie wiedzieć, ile czasu zajmie ukończenie zadania.
Reginaldo Camargo Ribeiro
Naprawdę powinieneś pomyśleć, zanim napiszesz jakikolwiek kod pff
DDD