Синхронная обработка задач в Gulp.JS

Gulp JS это менеджер задач ориентированный на фронтенд-разработку и ближайшая альтернатива Grunt JS.

Одно из нескольких отличий Gulp от Grunt заключается в том, что задачи (по умолчанию) запускаются асинхронно (вау, мне даже в словарь не пришлось заглядывать что бы написать это слово правильно с первого раза!). Приблизительно это означает, что все задачи запускаются одновременно.

Недавно я стал копаться в Gulp и одна из вещей с которыми больше всего пришлось побороться это несколько задач … которые надо было запускать синхронно.

В документации упоминается, как это делать, но после её прочтения несколько вопросов у меня все же оставалось, пока я с ними не разобрался.

Во первых, существуют 3 способа заставить задачу выполняться синхронно, но они должны использоваться вместе с зависимостями задач.

Первый из трех способов:

Передача колбека:

gulp.task('sync', function (cb) {  
    // setTimeout может быть любой асинхронной задачей
    setTimeout(function () {
        cb();
    }, 1000);
});

Возвращать поток:

gulp.task('sync', function () {  
    return gulp.src('js/*.js')
        .pipe(concat('script.min.js')
        .pipe(uglify())
        .pipe(gulp.dest('../dist/js');
});

Возвращать промис:

gulp.task('sync', function () {  
    var deferred = Q.defer();
    // setTimeout может быть любой асинхронной задачей
    setTimeout(function () {
        deferred.resolve();
    }, 1000);
    return deferred.promise;
});

Допустим, у нас есть задача, которая требует выполнения созданной нами выше задачи sync (подойдет любая из трех).

Недостаточно просто объявить задачу sync, как мы это сделали, нужно указать её в качестве зависимости другой задачи:

gulp.task('secondTask', ['sync'], function () {  
    // эта задача не запустится пока
    // задача sync не закончит работу!
});

В чем я ошибся, так это в том, что думал, что если я указываю несколько зависимостей они будут выполняться последовательно. Следующий код так НЕ РАБОТАЕТ:

gulp.task('thirdTask', function () {  
    // обратите внимание, что у задачи нет зависимостей
});

// Я надеялся, что запустится задача sync,
// ЗАТЕМ задача thirdTask и ЗАТЕМ — default,
// но ЭТО НЕ ТАК. Этот код запустит ОДНОВРЕМЕННО
// sync И thirdTask и затем — default.
gulp.task('default', ['sync', 'thirdTask'], function () {  
    // делаем всякое
});

Что бы запускать default так, как я хотел, нужно было, кроме всего прочего, объявить sync зависимостью thirdTask:

gulp.task('thirdTask', ['sync'] function () { 
    // теперь задача основана на sync. Если она 
    // возвращает поток, то default не запустится 
    // до тех пор, пока не закончит работу thirdTask
});

gulp.task('default', ['sync', 'thirdTask'], function () {  
    // делаем всякое
});

Обратите внимание на то, что будет происходить, если у вас есть задача watch, которая запускает thirdTask. Каждый раз, когда вы запускаете thirdTask, будет также запускаться sync, что может быть нежелательным!

В любом случае, надеюсь эта статья сможет помочь тем, кто пытается разобраться как запускать задачи в определенном порядке. И обратите внимание на то, что одна из вещей, которая делает Gulp быстрым и мощным это возможность запускать множество задач одновременно, так что запускайте их синхронно только тогда, когда это действительно нужно