笔者在使用gulp编译Angular项目时,由于编译服务器使用的是低版本Node JS,不支持同步执行脚本的方法execSync,导致只能使用异步加回调的方式执行,在这种情况下,如何保证异步任务顺序执行呢?官方给出的解决方案如下:
异步任务支持
任务可以异步执行,如果 fn 能做到以下其中一点:
接受一个 callback
// 在 shell 中执行一个命令
var exec = require('child_process').exec;
gulp.task('jekyll', function(cb) {
// 编译 Jekyll
exec('jekyll build', function(err) {
if (err) return cb(err); // 返回 error
cb(); // 完成 task
});
});
返回一个 stream
gulp.task('somename', function() {
var stream = gulp.src('client/**/*.js')
.pipe(minify())
.pipe(gulp.dest('build'));
return stream;
});
返回一个 promise
var Q = require('q');
gulp.task('somename', function() {
var deferred = Q.defer();
// 执行异步的操作
setTimeout(function() {
deferred.resolve();
}, 1);
return deferred.promise;
});
注意: 默认的,task 将以最大的并发数执行,也就是说,gulp 会一次性运行所有的 task 并且不做任何等待。如果你想要创建一个序列化的 task 队列,并以特定的顺序执行,你需要做两件事:
给出一个提示,来告知 task 什么时候执行完毕,
并且再给出一个提示,来告知一个 task 依赖另一个 task 的完成。
对于这个例子,让我们先假定你有两个 task,"one" 和 "two",并且你希望它们按照这个顺序执行:
在 "one" 中,你加入一个提示,来告知什么时候它会完成:可以再完成时候返回一个 callback,或者返回一个 promise 或 stream,这样系统会去等待它完成。
在 "two" 中,你需要添加一个提示来告诉系统它需要依赖第一个 task 完成。
因此,这个例子的实际代码将会是这样:
var gulp = require('gulp');
// 返回一个 callback,因此系统可以知道它什么时候完成
gulp.task('one', function(cb) {
// 做一些事 -- 异步的或者其他的
cb(err); // 如果 err 不是 null 或 undefined,则会停止执行,且注意,这样代表执行失败了
});
// 定义一个所依赖的 task 必须在这个 task 执行之前完成
gulp.task('two', ['one'], function() {
// 'one' 完成后
});
gulp.task('default', ['one', 'two']);
监测文件变化
假设脚本A执行编译命令,脚本B将A编译完成的文件拷贝到X目录,脚本C则将X目录下的文件打包,那么就可以使用监控文件的方式实现脚本的同步。
gulp.watch(glob[, opts, cb])
glob
类型: String or Array
一个 glob 字符串,或者一个包含多个 glob 字符串的数组,用来指定具体监控哪些文件的变动。
opts
类型: Object
传给 gaze 的参数。
cb(event)
类型: Function
每次变动需要执行的 callback。
gulp.watch('js/**/*.js', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
callback 会被传入一个名为 event 的对象。这个对象描述了所监控到的变动:
event.type
类型: String
发生的变动的类型:added, changed 或者 deleted。
event.path
类型: String
触发了该事件的文件的路径。
gulp-watch
值得注意的是官方的gulp.watch不支持监听文件的创建的删除,可以使用第三方插件gulp-watch来替代。
安装
npm install --save-dev gulp-watch
使用
var gulp = require('gulp'),
watch = require('gulp-watch');
gulp.task('stream', function () {
// Endless stream mode
return watch('css/**/*.css', { ignoreInitial: false })
.pipe(gulp.dest('build'));
});
gulp.task('callback', function () {
// Callback mode, useful if any plugin in the pipeline depends on the `end`/`flush` event
return watch('css/**/*.css', function () {
gulp.src('css/**/*.css')
.pipe(gulp.dest('build'));
});
});
合并任务脚本
如果只是执行多个批处理脚本,实际上完全可以通过合并脚本的方式实现同步,而且简化了大量代码。即便在不允许合并脚本的情况下,也可以通过把多个task合并成一个,然后在每个脚本的callback中执行下一个版本的方式实现同步执行,当然前提是每个异步方法都有回调函数:
|脚本A| ------callback----->|脚本B| ------callback----->|脚本C|
原生文件命令
gulp的task和watch命令实际上也只是对NodeJS的API的封装,那么我们可不可以使用NodeJS的原生命令实现同样的效果呢?答案当然是肯定的。
首先引用FileSystem库
var fs = require('fs');
然后以文件作为信号,A任务使用open命令创建文件,B任务定时用exists判断文件是否存在,存在时用watch命令监听文件名变化事件,文件名发生变化时执行脚本,并删除文件。完整流程如下:
文件同步流程图











网友评论