This tutorial has a new version, check it out!

Task Order

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $6.00

Look at the default task. The array defines task dependencies: Gulp runs each of these first, waits for them to finish, and then executes the callback for the task, if there is one. And based on the output, it looks like it runs them in order: clean, then styles, then scripts.

Is that true?

There is no Order to Dependent Tasks

Log a message once fonts is done:

134 lines gulpfile.js
... lines 1 - 112
gulp.task('fonts', function() {
app.copy(
config.bowerDir+'/font-awesome/fonts/*',
'web/fonts'
).on('end', function() {console.log('finished fonts!')});
});
... lines 119 - 134

And also add a message right when the watch task starts:

134 lines gulpfile.js
... lines 1 - 126
gulp.task('watch', function() {
console.log('starting watch!');
gulp.watch(config.assetsDir+'/'+config.sassPattern, ['styles']);
gulp.watch(config.assetsDir+'/js/**/*.js', ['scripts']);
});
... lines 132 - 134

The default task defines fonts then watch, and I want to see if that order matters.

Ok, try it!

gulp

It exploded! It say we're calling on on something undefined. This happens with our code because up in app.copy, we're not returning the stream. So yea, that would be undefined:

134 lines gulpfile.js
... lines 1 - 48
app.copy = function(srcFiles, outputDir) {
return gulp.src(srcFiles)
.pipe(gulp.dest(outputDir));
};
... lines 53 - 134

Ok, now try it. It's all out of order! Even though fonts is listed before watch in the dependency list, watch starts way before fonts finishes. In reality, Gulp reads the dependent tasks for default, then starts them all at once. Once they all finish, default runs.

But what if we needed fonts to finish before watch started? Well, it's the same trick: add fonts as a dependency to watch:

134 lines gulpfile.js
... lines 1 - 126
gulp.task('watch', ['fonts'], function() {
... lines 128 - 130
});
... lines 132 - 134

Try it out:

gulp

But surprise! It's still running out of order. Here's the reason: if you're dependent on a task like fonts, that task must return a Promise or a Gulp stream. If it doesn't, Gulp actually has no idea when fonts finishes

  • so it just runs watch right away. So, return app.copy from the fonts task, since app.copy returns a Gulp stream.

134 lines gulpfile.js
... lines 1 - 112
gulp.task('fonts', function() {
return app.copy(
... lines 115 - 116
).on('end', function() {console.log('finished fonts!')});
});
... lines 119 - 134

Now, Gulp can know when fonts truly finishes its work.

Ok, try it once more:

gulp

There it is! fonts finishes, and then watch starts. And there's one more thing: Gulp finally prints "Finished 'fonts'" in the right place, after fonts does its work.

Why? It's not that Gulp was lying before about when things finished. It's that Gulp can't report when a task finishes unless that task returns a Promise or a Gulp stream. This means we should return one of these from every task.

We don't need the fonts dependency, so take it off. And remove the logging:

133 lines gulpfile.js
... lines 1 - 112
gulp.task('fonts', function() {
return app.copy(
config.bowerDir+'/font-awesome/fonts/*',
'web/fonts'
);
});
... lines 119 - 126
gulp.task('watch', function() {
... lines 128 - 129
});
... lines 131 - 133

So if we should always return a stream or promise, how can we do that for styles? It doesn't have a single stream - it has two that are combined into the pipeline. We need to wait until both of them are finished.

Oh, the answer is so nice: just return pipeline.run():

133 lines gulpfile.js
... lines 1 - 84
gulp.task('styles', function() {
var pipeline = new Pipeline();
... lines 87 - 98
return pipeline.run(app.addStyle);
});
gulp.task('scripts', function() {
var pipeline = new Pipeline();
... lines 104 - 109
return pipeline.run(app.addScript);
});
... lines 112 - 133

This isn't magic. I wrote the Pipeline code, and I made run() return a Promise that resolves once everything is done. And if you know anything about promises, the guts should make sense to you. But if you have questions, just ask in the comments.

Make sure we didn't break anything.

gulp

Yep, it all still looks great. So if you eventually need to create a task that's dependent on styles or scripts finishing first, it'll work.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "2.6.*", // v2.6.4
        "doctrine/orm": "~2.2,>=2.2.3", // v2.4.6
        "doctrine/doctrine-bundle": "~1.2", // v1.2.0
        "twig/extensions": "~1.0", // v1.2.0
        "symfony/assetic-bundle": "~2.3", // v2.5.0
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.7
        "symfony/monolog-bundle": "~2.4", // v2.6.1
        "sensio/distribution-bundle": "~3.0", // v3.0.9
        "sensio/framework-extra-bundle": "~3.0", // v3.0.3
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "hautelook/alice-bundle": "~0.2" // 0.2
    },
    "require-dev": {
        "sensio/generator-bundle": "~2.3" // v2.4.0
    }
}