JavaScript错误捕获相关总结

try-catch ?

try-catch的好处:增强代码的健壮性而不必影响程序的主逻辑,保持代码的简洁清晰

通常情况下,使用者(包括用户、代码库的使用者)所引发的错误,需要通过异常机制来处理
因为异常发生的时候,原订的执行流程就无法继续,但对于用户来讲,他们不能因为这样的错误就终止程序的使用,所以提供给程序设计者异常机制,让设计者决定发生意外的时候应该做些什么。而这种意外的产生原因是用户,用户的操作千千万万,导致的结果也可能千奇百怪,但是他们的操作若使得原有流程无法继续,那么就是异常。

楼主说的判断文件先存在,再读写文件,其实就是这个问题,按照程序的流程,可以保证在判断是否存在的时候,文件的存在性,但是不能保证在真正操作文件的时候文件的存在性(例如判断的时候文件还在,真正操作之前却被用户自己删掉了)。因为流程上无法对流程外的用户行为(用户删文件)作出保证,所以需要异常机制。

我认为,用户在程序运行时触发所导致的错误,需要异常机制来捕捉和处理。

作者:Blueve
链接:https://www.zhihu.com/question/27122172/answer/35335950
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

try-catch用来处理可预知的异常

可预知

try-catch仅能用来处理可预知的错误,如:

楼主说的判断文件先存在,再读写文件,其实就是这个问题,按照程序的流程,可以保证在判断是否存在的时候,文件的存在性,但是不能保证在真正操作文件的时候文件的存在性(例如判断的时候文件还在,真正操作之前却被用户自己删掉了)。因为流程上无法对流程外的用户行为(用户删文件)作出保证,所以需要异常机制。
如果catch到不可预知的错误,就无法合理处理错误,这时就需要throw到上一层,由上层统一处理。

处理

try-catch后必须要处理错误,如果该错误无法处理,请不要使用try-catch,直接将error暴露给上层,由上层统一处理。不然会导致无法正常定位错误。
反例:

1
2
3
4
5
try {
doAnything();
} catch (e) {
// did nothing;
}

promise.catch ?

在一个promise链中,只要任何一个promise被reject,promise链就被破坏了,reject之后的promise都不会再执行,而是直接调用.catch方法。正确使用.catch有利于定位错误在promise链中的位置。
处理示例:
直接处理错误或将错误重新抛出为更高等级的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
// ...
.then((output) => {
return doAsyncOperation3();
})
.catch((err) => {
// Re-throw the error as a higher-level error.
// We will include the message from the lower-level
// error as part of the error message for this error.
throw new Error('Higher-level error. ' + err.message);
})
.catch((err) => {
// In here we will get the higher-level error.
})

throw error ?

从技术上将,我们可以throw任何类型值。但应尽量与内置对象达到兼容(throw有 namemessage 属性的对象)。
JavaScript 内置了许多标准错误对象类型:ErrorSyntaxErrorReferenceErrorTypeError

1
2
3
let error = new Error(message);
let error = new SyntaxError(message);
let error = new ReferenceError(message);

对于内置的错误对象,name 属性正是对应构造函数名(如:Error,syntaxError,ReferenceError),message 属性值为传入的参数值。
用例:

1
2
3
4
5
6
7
8
getAppname() {
if (this.pkg.name) {
debug('Loaded appname(%s) from package.json', this.pkg.name);
return this.pkg.name;
}
const pkg = path.join(this.options.baseDir, 'package.json');
throw new Error(`name is required from ${pkg}`);
}

assert ?

程序设计中还有一种叫断言(ASSERT)的东西,这种机制是用来约束程序设计者的,例如某些库的某些函数,在文档中约定了,这个函数的参数必须是>0,那么你在编程的时候愣是硬生生输入一个0,那么这时候就应该选择断言,用于帮助程序设计者及早的发现自己程序中的错误(这种错误是设计上的错误所引发的,而非用户的操作所导致的),而不是用异常机制去处理。
所以,由程序员设计不足所导致的错误,需要用断言来捕捉和处理。

作者:Blueve
链接:https://www.zhihu.com/question/27122172/answer/35335950
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class ContextLoader extends FileLoader {

/**
* @constructor
* @param {Object} options - options same as {@link FileLoader}
* @param {String} options.fieldClass - determine the field name of inject object.
*/
constructor(options) {
assert(options.property, 'options.property is required');
assert(options.inject, 'options.inject is required');
const target = options.target = {};
if (options.fieldClass) {
options.inject[options.fieldClass] = target;
}
super(options);

const app = this.options.inject;
const property = options.property;

// define ctx.service
Object.defineProperty(app.context, property, {
get() {
// distinguish property cache,
// cache's lifecycle is the same with this context instance
// e.x. ctx.service1 and ctx.service2 have different cache
if (!this[CLASSLOADER]) {
this[CLASSLOADER] = new Map();
}
const classLoader = this[CLASSLOADER];

let instance = classLoader.get(property);
if (!instance) {
instance = getInstance(target, this);
classLoader.set(property, instance);
}
return instance;
},
});
}
}
0%