我有一个gulp任务,该任务循环遍历一个文件夹以查找子文件夹,并根据每个文件夹的内容输出一个JavaScript文件。下面是一个更直观的示例。
成为:
这是通过读取src/assets/scripts
目录的内容,然后对每个文件夹(critical
,legacy
,modern
,service-worker
),然后将每个文件夹的内容发送到与merge-stream合并在一起的Gulp任务。
所有这些都很好用,除了一旦任务合并回一起,如果编译成功,我想触发通知。如果我尝试将任何内容通过管道传输到合并的流,那么它将不起作用。它只会返回合并的流,并且永远不会继续。
如果我取消对PROCESS_SCRIPTS
函数的限制,并且不使用merge-stream(即仅处理一个手动指定的文件夹),则它可以正常工作,所以我对所发生的事情感到迷惑。
这是我的全部任务:
module.exports = {
scripts(gulp, plugins, ran_tasks, on_error) {
// task-specific plugins
const ESLINT = require("gulp-eslint");
const WEBPACK = require("webpack-stream");
// process scripts
const PROCESS_SCRIPTS = (js_directory, destination_file_name = "modern.js", compare_file_name = "modern.js", source = [global.settings.paths.src + "/assets/scripts/*.js"]) => {
return new Promise((resolve, reject) => {
const WEBPACK_CONFIG = {
mode: "development",
};
// update webpack config for the current target destination and file name
WEBPACK_CONFIG.mode = plugins.argv.dist ? "production" : WEBPACK_CONFIG.mode;
WEBPACK_CONFIG.output = {
filename: destination_file_name
};
const TASK = gulp.src(source)
// prevent breaking on error
.pipe(plugins.plumber({errorHandler: on_error}))
// check if source is newer than destination
.pipe(plugins.newer(js_directory + "/" + compare_file_name))
// lint all scripts
.pipe(ESLINT())
// print lint errors
.pipe(ESLINT.format())
// run webpack
.pipe(WEBPACK(WEBPACK_CONFIG))
// generate a hash and add it to the file name
.pipe(plugins.hash({template: "<%= name %>.<%= hash %><%= ext %>"}))
// output scripts to compiled directory
.pipe(gulp.dest(js_directory))
// generate a hash manfiest
.pipe(plugins.hash.manifest(".hashmanifest-scripts", {
deleteOld: true,
sourceDir: js_directory
}))
// output hash manifest in root
.pipe(gulp.dest("."))
// reject after errors
.on("error", () => {
reject(TASK);
})
// return the task after completion
.on("end", () => {
resolve(TASK);
});
});
};
// scripts task, lints, concatenates, & compresses JS
return new Promise ((resolve) => {
// set JS directory
const JS_DIRECTORY = plugins.argv.dist ? global.settings.paths.dist + "/assets/scripts" : global.settings.paths.dev + "/assets/scripts";
// set the source directory
const SOURCE_DIRECTORY = global.settings.paths.src + "/assets/scripts";
// set up an empty merged stream
const MERGED_STREAMS = plugins.merge();
// get the script source folder list
const SCRIPT_FOLDERS = plugins.fs.readdirSync(SOURCE_DIRECTORY);
// get the script destination file list
const SCRIPT_FILES = plugins.fs.existsSync(JS_DIRECTORY) ? plugins.fs.readdirSync(JS_DIRECTORY) : false;
// process all the script folders
const PROCESS_SCRIPT_FOLDERS = () => {
return Promise.resolve().then(() => {
// shift to the next folder
const FOLDER_NAME = SCRIPT_FOLDERS.shift();
// find the existing destination script file name
const FILE_NAME = SCRIPT_FILES ? SCRIPT_FILES.find((name) => {
return name.match(new RegExp(FOLDER_NAME + ".[a-z0-9]{8}.js"));
}) : FOLDER_NAME + ".js";
// process all scripts, update the stream
return PROCESS_SCRIPTS(JS_DIRECTORY, FOLDER_NAME + ".js", FILE_NAME, SOURCE_DIRECTORY + "/" + FOLDER_NAME + "/**/*").then((processed) => {
MERGED_STREAMS.add(processed);
});
}).then(() => SCRIPT_FOLDERS.length > 0 ? PROCESS_SCRIPT_FOLDERS() : resolve());
};
PROCESS_SCRIPT_FOLDERS().then(() => {
// wrap up
return MERGED_STREAMS
// prevent breaking on error
.pipe(plugins.plumber({
errorHandler: on_error,
}))
// notify that task is complete, if not part of default or watch
.pipe(plugins.gulpif(gulp.seq.indexOf("scripts") > gulp.seq.indexOf("default"), plugins.notify({
title: "Success!",
message: "Scripts task complete!",
onLast: true,
})))
// push task to ran_tasks array
.on("data", () => {
if (ran_tasks.indexOf("scripts") < 0) {
ran_tasks.push("scripts");
}
})
// resolve the promise on end
.on("end", () => {
resolve();
});
});
});
}
};
在我的GitHub上也可见:https://github.com/JacobDB/new-site/blob/master/gulp-tasks/scripts.js
编辑:我已经尝试了几件事,在这里将详细介绍。
console.log("hello world")
永远不会在MERGED_STREAMS.on("data")
,MERGED_STREAMS.on("error")
或MERGED_STREAMS.on("end")
之后触发。const MERGED_STREAMS = plugins.merge();
移动到模块级变量(即紧随const WEBPACK = require("webpack-stream")
之后)不会改变结果。MERGED_STREAMS.add(gulp.src(source) ...)
而不是添加流不会改变结果,除非离开.pipe(gulp.dist("."))
,这是输出.hashmanifest
所必需的,并始终将任务标记为已运行。webpack
,hash
或eslint
都没有任何作用。PROCESS_SCRIPTS
更改为返回返回流的承诺,然后将每个文件夹作为单独的变量进行处理,然后手动进行合并似乎确实可以正确触发任务的运行,但是webpack
只能运行一次,因此仅输出一个文件– critical.hash.js
。 注意:我尚未结合禁用hash
来测试此方法,如果始终输出.hashmanifest
,可能会导致该方法被标记为正确运行。 lint
任务不是承诺,在控制台中导致unexpected end of stream
错误。编辑2:已根据@Louis的建议对任务进行了修订。
答案 0 :(得分:3)
上面的代码有很多问题。使代码难以遵循和调试的一个主要问题是在不需要的地方使用new Promise
。 通常,如果您有new Promise
,并且诺言执行者内部的逻辑会根据另一个诺言的结果来解决或拒绝 ,那么您无需使用{{1 }}。
有时候人们有这样的代码:
new Promise
假设function foo() {
const a = getValueSynchronously();
const b = getOtherValueSynchronously();
return doSomethingAsynchronous(a, b).then(x => x.someMethod());
}
返回一个承诺。上面的doSomethigAsynchronous
函数的问题在于,如果foo
和getValueSynchronously
失败,则getOtherValueSynchronously
会引发异常,但是如果foo
失败,则它将异常拒绝诺言。因此,使用doSomethingAsynchronous
的代码如果要处理所有可能的失败,则必须处理同步异常和异步拒绝。有时,人们认为他们可以通过使所有失败都成为承诺拒绝来解决问题:
foo
在上面的代码中,如果function foo() {
return new Promise((resolve, reject) => {
const a = getValueSynchronously();
const b = getOtherValueSynchronously();
doSomethingAsynchronous(a, b).then(x => x.someMethod()).then(resolve, reject);
});
}
或getValueSynchronously
失败,那就是承诺被拒绝。但是上面代码的问题是很容易弄错它。您可能会忘记在需要的任何地方调用getOtherValueSynchronously
。 (实际上,您在代码中 do 出现了此错误。您已经嵌套了承诺,其承诺不会被传播出去。它们只是丢失了,这意味着如果发生错误您的代码将停止,而您不知道为什么。)或者您可能会想在嵌套函数中向下调用'resolve way',这使逻辑难以理解。
您也可以这样做:
reject
您可以使用function foo() {
return Promise.resolve().then(() => {
const a = getValueSynchronously();
const b = getOtherValueSynchronously();
return doSomethingAsynchronous(a, b);
}).then(x => x.someMethod());
}
进入承诺的世界(嗯……“应许之地?”)。在上面的代码中,您不必记住调用Promise.resolve()
。如果reject
回调中的代码由于任何原因失败 ,您将获得被拒绝的承诺。
我还注意到,在许多地方,您从传递给.then
的执行程序函数中返回一个值。如果您不在那里使用new Promise
,则代码的行为将完全相同。为了说明,此代码:
return
与代码完全一样 :
function foo() {
return new Promise((resolve, reject) => {
return doSomethingAsynchronous().then(resolve, reject);
});
}
从执行程序返回的值将被忽略。故事结局。如果您认为从执行者那里获得的价值正在做某事,那是不正确的。