Optimizing Build Times with gulp.series and gulp.parallel
Learn how to combine gulp.series and gulp.parallel to speed up your build pipeline while avoiding common pitfalls.
09 Jul 2025, 19:33 UTC

Problem: Unnecessary Serialization Slows Your Build
Many Gulp pipelines run every task one after another, even when tasks do not depend on each other’s output. This serial execution inflates total build time, especially on CI runners where minutes matter.
Thesis: Match Execution Mode to Task Dependencies
By using gulp.series() for tasks that must run sequentially and gulp.parallel() for independent tasks, you can cut wall‑clock time while preserving correctness.
Understanding the Two Composition Helpers
gulp.series(taskA, taskB)– guaranteestaskBstarts only aftertaskAsignals completion (stream end, promise resolve, or callback).gulp.parallel(taskA, taskB)– starts both tasks at the same time; the composite task finishes when the slowest of them ends.
Both helpers require each leaf task to return a stream, a promise, or invoke its callback. Forgetting this leads to the dreaded "async completion" error or a hanging pipeline.
Worked Example: A Typical Web Build
Consider a project that needs to clean the dist folder, compile Sass, transpile ES6 JavaScript, run unit tests, and finally copy assets. Cleaning must precede compilation, but Sass and JS compilation are independent. Tests need both compiled outputs, and copying can happen after tests.
const { src, dest, series, parallel } = require('gulp');
const del = require('del');
const sass = require('gulp-sass')(require('sass'));
const babel = require('gulp-babel');
const mocha = require('gulp-mocha');
function clean() {
return del(['dist']);
}
function styles() {
return src('src/scss/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(dest('dist/css'));
}
function scripts() {
return src('src/js/**/*.js')
.pipe(babel({ presets: ['@babel/env'] }))
.pipe(dest('dist/js'));
}
function test() {
return src('test/**/*.js', { read: false })
.pipe(mocha());
}
function copyAssets() {
return src('src/assets/**/*').pipe(dest('dist/assets'));
}
// Build task: clean → (styles & scripts in parallel) → test → copyAssets
exports.build = series(
clean,
parallel(styles, scripts),
test,
copyAssets
);
To verify the timing locally, run:
# Install dev dependencies (run once)
npm init -y
npm install --save-dev gulp gulp-sass gulp-babel gulp-mocha del
# Execute the build and measure elapsed time
time npx gulp build
If you replace parallel(styles, scripts) with series(styles, scripts) and re‑run, you will observe the build time increase roughly by the duration of the slower of the two tasks, demonstrating the gain from parallel execution.
Trade‑off and Limitation
Running many CPU‑ or memory‑intensive tasks in parallel can saturate the CI runner, leading to longer overall times or out‑of‑memory kills. A practical guardrail is to monitor system resources (e.g., top or CI metrics) and limit the degree of parallelism to the number of available cores. For extremely heavy tasks, consider breaking them into smaller chunks or offloading to a dedicated build step.
Actionable Closing
- Audit your
gulpfile.jsand mark each task’s dependencies. - Replace sequential groups of independent tasks with
gulp.parallel(). - Keep dependent steps wrapped in
gulp.series(). - Run the build locally with
timeand compare before/after. - On CI, watch CPU/memory usage; if spikes appear, reduce the parallel set or add a concurrency limit via a wrapper like
gulp-parallel-limit.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.