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.
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:
// ... 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:
// ... 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:
// ... 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
:
// ... 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 thefonts
task, sinceapp.copy
returns a Gulp stream.
// ... 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:
// ... 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()
:
// ... 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.
Hey Ryan. Very detailed tutorial, thanks. Switched from Assetic to Gulp.
I have experienced small problem with browserSync and cache busting - browserSync forces full page reload with versioning in dev environment. (Instead of Css injection) So, I have modified config: (All files are commented)
https://github.com/nix23/gu...
Shortly, I have added/modified following:
1) Added ability to execute console commands in order with Pipeline class; (If some command fails, command execution stops)
2) Added browserSync Gulp integration with Css injection support;
3) Modified AssetExtension class - now css/js assets are versioned only in prod environment, because dynamic name change will force browserSync to perform full page reload instead of Css injection on sass/css file changes;
4) Extended FosJsRoutingBundle to override initialize method in dump command and inject target arg to InputInterface object. (We want apply versioning to 'fos_js_routes.js' file as well on production environment);
Hope, this helps someone. ;)
Cheers!