ES6项目控制台无错误输出问题排查(Gulp+Babel环境)
问题场景描述
我最近用ES6开发项目,搭配Gulp+Babel(require('gulp-babel');)做代码转译,遇到个特别头疼的问题——代码明明出错导致程序停了,但控制台完全没输出任何错误信息,根本摸不着头脑。
举个具体例子:我写了继承自父类Event的子类Quest,父类只有method1()方法,但子类构造函数里偏偏调用了父类不存在的super.method2()。结果程序直接卡停,console.log('DONE')永远不会执行,可控制台连个报错提示都没有,完全不知道问题出在哪。
相关代码片段
异步调用代码
API.Quest.getQuest(1).then((quest) => { this.quest = new Quest(quest); console.log('DONE'); // 永远不会执行到这里 }, (err) => { console.log('ERR', err); });
类定义代码
class Event { constructor(args, childEventClass, childEventClassName) { this.name = args.name; this.id = args.id; this.children = args[childEventClassName].map(eventObj => new childEventClass(eventObj)); // ... 其他代码 } method1() { console.log('Called parent method 1'); } } class Quest extends Event { constructor(args) { super(args, Task, 'tasks'); super.method2(); // 这里调用了父类不存在的方法 } }; class Task extends Event { // ... 其他代码 };
Gulp配置代码
const babel = require('gulp-babel'); // ... 其他依赖 gulp.task('scripts', function() { return gulp.src([ 'js/**/*.js', ]) .pipe(babel({ presets: ['es2015'] })) .pipe(concatJs('main.js')) .pipe(gulp.dest('build/js')); });
补充的错误捕获代码(Edit 2)
API.Quest.getQuest(1).then((quest) => { // ... }, (err) => { console.error('REJECT ERR', err); // 捕获Promise拒绝的错误 }).catch((err) => { // 捕获? console.error('CATCH ERR', err); });
问题原因分析
咱们一步步拆解这个坑:
构造函数报错的特殊性:子类
Quest构造函数里的super.method2()调用了父类不存在的方法,这会直接抛出TypeError——但这个错误是在Promise的then成功回调里的同步代码中抛出的。Promise的机制是:回调里的同步错误会被自动捕获,并转为Promise的拒绝状态,不会直接在控制台抛出。错误捕获的遗漏:你最开始写的
then(success, fail),第二个回调只负责捕获前面Promise链本身拒绝的错误,完全管不到success回调里自己抛出的同步错误;后来补充的.catch()本来能捕获,但可能因为转译或Promise链路的问题,没生效(或者你没正确触发到它)。Babel转译的隐藏影响:ES2015类转译成ES5代码后,构造函数里的错误被包裹在Promise回调中时,转译后的代码可能不会直接把错误暴露到控制台,必须通过正确的Promise错误捕获链路才能拿到。
解决方案
针对这个问题,咱们从几个层面解决:
1. 完善Promise错误捕获链路
把异步调用代码改成下面这样,确保所有错误都能被兜底捕获:
API.Quest.getQuest(1) .then((quest) => { this.quest = new Quest(quest); console.log('DONE'); }) .catch((err) => { // 这里能捕获所有错误:Promise自身的拒绝 + then回调里的同步报错 console.error('全局捕获错误:', err); console.error(err.stack); // 打印错误栈,直接定位到原代码的出错行 });
划重点:
then(success, fail)的fail回调管不到success回调里的错误,只有.catch()能捕获整个Promise链上的所有错误。
2. 开发阶段启用SourceMap定位
在Gulp的Babel配置里加上SourceMap,这样转译后的代码报错时,能直接映射回原ES6代码的行号,不用对着转译后的晦涩代码找问题:
// 先安装依赖:npm install gulp-sourcemaps --save-dev const sourcemaps = require('gulp-sourcemaps'); gulp.task('scripts', function() { return gulp.src([ 'js/**/*.js', ]) .pipe(sourcemaps.init()) // 初始化sourcemap .pipe(babel({ presets: ['es2015'] })) .pipe(concatJs('main.js')) .pipe(sourcemaps.write('./')) // 把sourcemap文件输出到build/js目录 .pipe(gulp.dest('build/js')); });
3. 提前规避类调用错误
开发时可以给父类加个兜底方法,或者在子类调用前做检查,避免这种低级错误:
// 方案1:父类加兜底方法 class Event { // ... 其他代码 method2() { console.warn('警告:父类Event没有实现method2方法,你是不是写错方法名了?'); } } // 方案2:子类调用前先判断 class Quest extends Event { constructor(args) { super(args, Task, 'tasks'); if (typeof super.method2 === 'function') { super.method2(); } else { throw new Error('错误:父类Event不存在method2方法'); } } };
4. 开启浏览器的未捕获Promise提示
现代浏览器默认会提示未捕获的Promise拒绝,但可能被你不小心禁用了。打开浏览器控制台的设置(比如Chrome的Settings > Console),找到"Uncaught exceptions"相关选项并开启,这样即使没写.catch(),浏览器也会把未捕获的Promise拒绝打印出来。
内容的提问来源于stack exchange,提问作者user3871

