Gulp JS任务报错NodeError:回调函数被多次调用
Gulp任务
js_bundle报错:NodeError: Callback function called multiple times 我有五个Gulp JS任务,其中名为js_bundle的任务出现如下报错:
NodeError: Callback function called multiple times
以下是我的gulpfile.js完整代码:
const { src, dest, parallel , series , watch } = require('gulp'); const sass = require('gulp-sass')(require('sass')); const fileinclude = require('gulp-file-include'); const sourcemaps = require('gulp-sourcemaps'); const uglify = require('gulp-uglify'); const minify = require('gulp-minifier'); const strip = require('gulp-strip-comments'); const rtlcss = require('gulp-rtlcss'); const rename = require('gulp-rename'); // sass.compiler = require('node-sass'); // no-need for gulp-sass v5+ var node_path = '../../../'; function html(cb) { src('html/src/**') .pipe(dest('html/dist/')); cb(); } function scss(cb) { src(['scss/*.scss']) // .pipe(sourcemaps.init()) // If you want generate source map. .pipe(sass().on('error', sass.logError)) // uses {outputStyle: 'compressed'} in saas() for minify css // .pipe(sourcemaps.write('./')) // If you want generate source map. .pipe(dest('assets/dist/css')); src(['scss/*.scss', '!scss/style-email.scss']) // .pipe(sourcemaps.init()) // If you want generate source map. .pipe(sass().on('error', sass.logError)) // uses {outputStyle: 'compressed'} in saas() for minify css // .pipe(sourcemaps.write('./')) // If you want generate source map. .pipe(rtlcss()) .pipe(rename({ suffix: '.rtl' })) .pipe(dest('assets/dist/css')); // EDITORS src(['scss/editors/*.scss']) // .pipe(sourcemaps.init()) // If you want generate source map. .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError)) // remove {outputStyle: 'compressed'} from saas() if do not want to minify css // .pipe(sourcemaps.write('./')) // If you want generate source map. .pipe(dest('assets/dist/css/editors')); src(['scss/editors/*.scss']) // .pipe(sourcemaps.init()) // If you want generate source map. .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError)) // remove {outputStyle: 'compressed'} from saas() if do not want to minify css // .pipe(sourcemaps.write('./')) // If you want generate source map. .pipe(rtlcss()) .pipe(rename({ suffix: '.rtl' })) .pipe(dest('assets/dist/css/editors')); src(['scss/libs/*.scss']) // .pipe(sourcemaps.init()) // If you want generate source map. .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError)) // remove {outputStyle: 'compressed'} from saas() if do not want to minify css // .pipe(sourcemaps.write('./')) // If you want generate source map. .pipe(dest('assets/dist/css/libs')); // SKINS src(['scss/skins/*.scss']) // .pipe(sourcemaps.init()) // If you want generate source map. .pipe(sass().on('error', sass.logError)) // uses {outputStyle: 'compressed'} in saas() for minify css // .pipe(sourcemaps.write('./')) // If you want generate source map. .pipe(dest('assets/dist/css/skins')); cb(); } function js_scripts(cb) { src(['assets/src/js/*.js','!assets/src/js/bundle.js']) // .pipe(uglify()) // If you minify the code. .pipe(dest('assets/dist/js')); src(['assets/src/js/charts/*.js']) .pipe(uglify()) // If you want minify the code. .pipe(dest('assets/dist/js/charts')); src(['assets/src/js/apps/*.js']) // .pipe(uglify()) // If you minify the code. .pipe(dest('assets/dist/js/apps')); cb(); } /* The task giving me the error */ function js_bundle(cb) { src('assets/src/js/bundle.js') .pipe(fileinclude({ prefix: '@@', basepath: '@file', context: { build: 'dist', nodeRoot: node_path } })) .pipe(strip()) .pipe(minify({ minify: true, minifyJS: { sourceMap: false } })) // Disable, if you dont want to minify bundle file. .pipe(dest('assets/dist/js')); src(['assets/src/js/libs/**', '!assets/src/js/libs/editors/skins/**']) .pipe(fileinclude({ prefix: '@@', basepath: '@file', context: { build: 'dist', nodeRoot: node_path } })) .pipe(strip()) .pipe(minify({ minify: true, minifyJS: { sourceMap: false } })) // Disable, if you dont want to minify bundle file. .pipe(dest('assets/dist/js/libs')); src('assets/src/js/libs/editors/skins/**').pipe(dest('assets/dist/js/libs/editors/skins')); cb(); } function assets(cb){ src(['assets/src/**', '!assets/src/js/**', '!assets/src/css/**']) .pipe(dest('assets/dist/')); cb(); } exports.build = series(html, scss, js_scripts, /* The task giving me the error */ js_bundle, assets); exports.develop = function() { watch(['scss/*.scss','scss/**'], scss) watch(['html/src/*.html','html/src/**/*.html'], html) watch(['assets/src/**'], assets) watch(['assets/src/js/*.js','assets/src/js/charts/*.js', 'assets/src/js/apps/*.js', '!assets/src/js/bundle.js'], js_scripts) watch(['assets/src/js/libs/**','assets/src/js/bundle.js'], js_bundle) };
经排查,引发报错的核心代码片段是:
.pipe(fileinclude({ prefix: '@@', basepath: '@file', context: { build: 'dist', nodeRoot: node_path } }))
已尝试的解决方法(均无效)
- 将
gulp-file-include插件更新至最新版本; - 日志排查回调函数调用情况,未发现异常;
- 移除
strip和minify步骤后仍报错,确认gulp-file-include是可疑根源; - 参考相关问题,但未使用文中提到的
gulp-imagemin插件。
解决方案
问题出在你使用**回调函数(cb)**的方式上:Gulp任务中如果存在多个异步流(比如多个src()调用),直接调用cb()会导致任务在异步流完成前就结束,而后续异步流的回调可能会触发多次cb,从而引发错误。
正确的做法是将多个异步流合并,或者使用返回流/Promise的方式处理异步操作。这里推荐使用parallel来合并多个流,确保所有异步操作完成后再结束任务:
修改js_bundle任务如下:
const { src, dest, parallel , series , watch } = require('gulp'); // ... 保留其他依赖 /* 修改后的js_bundle任务 */ function js_bundle() { // 定义三个异步流任务 const bundleMain = src('assets/src/js/bundle.js') .pipe(fileinclude({ prefix: '@@', basepath: '@file', context: { build: 'dist', nodeRoot: node_path } })) .pipe(strip()) .pipe(minify({ minify: true, minifyJS: { sourceMap: false } })) .pipe(dest('assets/dist/js')); const bundleLibs = src(['assets/src/js/libs/**', '!assets/src/js/libs/editors/skins/**']) .pipe(fileinclude({ prefix: '@@', basepath: '@file', context: { build: 'dist', nodeRoot: node_path } })) .pipe(strip()) .pipe(minify({ minify: true, minifyJS: { sourceMap: false } })) .pipe(dest('assets/dist/js/libs')); const copySkins = src('assets/src/js/libs/editors/skins/**').pipe(dest('assets/dist/js/libs/editors/skins')); // 用parallel合并所有流,返回合并后的任务 return parallel(bundleMain, bundleLibs, copySkins); }
同理,你的其他任务(html、scss、js_scripts、assets)也存在同样的问题,建议一并修改,避免后续出现类似错误。比如修改html任务:
function html() { return src('html/src/**').pipe(dest('html/dist/')); }
原理说明
Gulp 4+要求异步任务必须通过以下方式之一告知任务完成:
- 返回一个流(Stream)
- 返回一个Promise
- 使用
async/await - 调用回调函数
cb(只能调用一次)
你之前的写法直接调用cb(),没有等待多个src()流完成,当gulp-file-include这类异步插件处理完成后,可能会触发额外的回调,导致cb被多次调用,从而抛出错误。改用返回流或合并流的方式,Gulp会自动跟踪所有异步操作的完成状态,避免重复调用回调的问题。
内容的提问来源于stack exchange,提问作者Codestrip
相关产品推荐
相关产品推荐

