为什么使用 Pump?
在使用 Node.js 流的 pipe
时,错误不会通过管道流向前传播,如果目标流关闭,源流也不会关闭。pump
模块规范化了这些问题,并在回调中将错误传递给你。
常见的 gulpfile 示例
gulp 文件中的一种常见模式是简单地返回一个 Node.js 流,并期望 gulp 工具处理错误。
// 常见的 gulpfile 示例
var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task('compress', function () {
// 返回一个 Node.js 流,但没有错误消息处理
return gulp.src('lib/*.js')
.pipe(uglify())
.pipe(gulp.dest('dist'));
});
JavaScript 文件中有一个错误,但错误消息完全没有帮助。你想知道哪个文件和哪一行包含错误。那么这一团乱码是什么?
当流中出现错误时,Node.js 流会触发 'error' 事件,但如果没有这个事件的处理程序,它会转而使用定义的未捕获异常处理程序。未捕获异常处理程序的默认行为已有文档说明:
默认情况下,Node.js 通过将堆栈跟踪打印到 stderr 并退出来处理此类异常。
处理错误
由于让错误到达未捕获的异常处理程序没有用,我们应该正确处理异常。让我们快速尝试一下。
var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task('compress', function () {
return gulp.src('lib/*.js')
.pipe(uglify())
.pipe(gulp.dest('dist'))
.on('error', function(err) {
console.error('压缩任务中的错误', err.toString());
});
});
不幸的是,Node.js 流的 pipe
函数不会将错误转发到链中,所以这个错误处理程序只处理 gulp.dest
给出的错误。相反,我们需要为每个流处理错误。
var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task('compress', function () {
function createErrorHandler(name) {
return function (err) {
console.error('压缩任务中 ' + name + ' 的错误', err.toString());
};
}
return gulp.src('lib/*.js')
.on('error', createErrorHandler('gulp.src'))
.pipe(uglify())
.on('error', createErrorHandler('uglify'))
.pipe(gulp.dest('dist'))
.on('error', createErrorHandler('gulp.dest'));
});
在每个 gulp 任务中添加这么多复杂性很麻烦,而且很容易忘记这样做。此外,这仍然不完美,因为它没有正确地向 gulp 的任务系统发出任务失败的信号。我们可以修复这个问题,并处理流错误传播的其他棘手问题,但这需要更多的工作!
使用 pump
pump
模块是一种作弊码。它是 pipe
功能的包装器,可以为你处理这些情况,这样你就可以停止修改 gulpfile,回去为你的应用开发新功能。
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var pump = require('pump');
gulp.task('compress', function (cb) {
pump([
gulp.src('lib/*.js'),
uglify(),
gulp.dest('dist')
],
cb
);
});
gulp 任务系统为 gulp 任务提供了一个回调,它可以发出任务成功完成的信号(不带参数调用),或任务失败的信号(用 Error 参数调用)。幸运的是,这与 pump
使用的格式完全相同!
现在可以非常清楚地知道错误来自哪个插件,实际错误是什么,以及来自哪个文件和行号。