最近node写的比较多,后台应用你懂的,一个异常没处理好,分分钟crash给你看。在开发过程当中总结了一些经验,分享给你们node
Error类是JS的原生类,在平常开发中也很常见,也很简单,我在写文档以前去MDN
上查了下资料:api
Error类的用法很简单,new
或者直接把Error
当成function来用都行,而后在你认为须要抛出异常的地方throw
它。promise
JS中有几种内置的Error类型,好比最多见的ReferrenceError
,都继承自Error
,所以咱们本身也能够定义本身的错误类型,只须要继承Error便可,直接上MDN
的栗子:安全
class CustomError extends Error {
constructor(foo = 'bar', ...params) {
// Pass remaining arguments (including vendor specific ones) to parent constructor
super(...params);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError);
}
// Custom debugging information
this.foo = foo;
this.date = new Date();
}
}
try {
throw new CustomError('baz', 'bazMessage');
} catch(e){
console.log(e.foo); //baz
console.log(e.message); //bazMessage
console.log(e.stack); //stacktrace
}
复制代码
try...catch
就很少说了,这里须要提一下Promise
和await
的捕获方式async
Promise
里咱们通常在最后加一个.catch
,用来处理整个Promise执行链路中任何可能出现的异常,好比:函数
Promise.resolve()
.then(() => {
console.log(a); // 这里会出现异常
})
.then(() => {
console.log('hi'); // 这里不会执行
})
.catch(err => {
console.log(err); // ReferenceError
});
复制代码
await
语法返回的也是Promise对象,不过你能够经过try...catch
语法来接住异常post
async function sayHi() {
try {
let ret = await anotherPromiseFunction();
}
catch (err) {
console.log(err); // anotherPromiseFunction抛出的异常在这里处理
}
}
复制代码
自定义错误类
。JS原生的错误类型只能定义基本的语言类异常,而咱们在业务代码中,须要频繁地定义、抛出一些与业务强相关的异常,好比:校验验证码的api,验证码格式不对时须要抛出一个异常,这个异常应该是跟校验相关的,且调用者能清晰解读而且可以根据错误信息作出相应处理的。优化
个人自定义错误类:ui
/** * @file 错误类型汇总 * @author arlenyang */
class ApiError extends Error {
/** * @constructor * @param {string} code 错误码 * @param {string} msg 中文描述 */
constructor(code, msg) {
super(msg);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError);
}
this.code = code;
this.msg = msg;
}
toString() {
return `Api${this.stack}\n ${this.msg}, errCode: ${this.code}`;
}
}
// 错误类型
ApiError.MYSQL_QUERY_ERROR = 1;
ApiError.MYSQL_QUERY_ERROR_DESC = '查询数据失败';
// ......
复制代码
构造函数有两个参数,code
和message
。this
node
里很常见在这个类里,我用静态变量的形式存放全部的错误码和它的描述字段,其实也能够放在一个单独的存放静态变量的文件里
你还能够扩展你的异常类作更多相关的事情,好比记录错误日志,上报或者写入本地日志。
另外,你还能够自定义异常的输出,经过重写toString
方法。还记得以前提到过的error.stack
和Error.captureStackTrace
吗?你能够在toString
方法里优化异常的输出格式,加入额外的信息,等等
checked exception
)。而其余异常,多是咱们的代码自己有bug,也多是系统调用产生的error,这类异常须要调用者本身考虑了举个栗子,写一个读取文件内容的api。
/** * 读取文件 * @param {String} filepath 文件路径 * @return {Buffer} 文件内容 */
async function readFile(filepath) {
}
复制代码
根据这个api的行为能够预见几个异常:
至于可能出现调用系统读取文件的api出现的异常
、filepath不符合文件路径格式
等等的问题,都不是这个api应该考虑的范围。实现以下:
/** * 读取文件 * @param {String} filepath 文件路径 * @return {Buffer} 文件内容 */
async function readFile(filepath) {
// 检查filepath是否为空
if (!filepath) {
// 使用自定义错误类
throw new ApiError(
ApiError.PARAMETER_FORMAT_ERROR,
ApiError.PARAMETER_FORMAT_ERROR_DESC,
);
}
try {
let stat = await fs.stat(filepath);
// 检查对应的文件是否为文件类型
if (!stat.isFile()) {
throw new ApiError(
ApiError.FILE_FORMAT_ERROR,
ApiError.FILE_FORMAT_ERROR_DESC,
);
}
let content = await fs.readFile(filepath);
return content;
}
catch (err) {
// 检查文件是否存在
if (err.code === 'ENOENT') {
throw new ApiError(
ApiError.FILE_NOT_FOUND_ERROR,
ApiError.FILE_NOT_FOUND_ERROR_DESC,
);
}
throw err;
}
}
复制代码
对于promise的异常处理,千万不要为了'安全起见'把全部函数都.catch
,这可能会致使exception被吞掉,查错时找不到异常信息
对于须要catch的promise,尽可能先处理异常,处理不了的,再向后抛
用Promise.reject(error)
代替throw error
,更优雅
promise的then(resolve, reject)
和then(resolve, null).catch()
的区别
promise.catch
里若是没有再reject
或throw
,以后逻辑会走到resolve
里而非reject
Promise.resolve()
.then(() => {
console.log(a); // 这一行报错,会被catch接住
})
.catch(err => {
console.log(err);
return 1;
})
.then(ret => {
console.log(ret); // 会执行,且打印 1
});
复制代码