nodejs异常处理

来源:互联网 发布:帝国时代2知乎 编辑:程序博客网 时间:2024/05/20 05:05

Node.js是一个JavaScript运行时平台,其应用发生错误都是一个Error实例或Error子类的实例。

用new Error()创建一个错误对象实例, 主要有以下几个属性,message:错误消息,stack:错误堆栈信息,name:错误类型

一。在nodejs应用中,可能发生的错误有三大类

1.标准javascript错误:

也就是name的六种类型

BadRequestError: 请求错误

ReferenceError: 是引用一个不存在的变量时发生的错误。

SyntaxError: 是解析代码时发生的语法错误

RangeError: RangeError是当一个值超出有效范围时发生的错误。主要有几种情况,一是数组长度为负数,二是Number对象的方法参数超出范围,以及函数堆栈超      过最大值

TypeError : TypeError是变量或参数不是预期类型时发生的错误。比如,对字符串、布尔值、数值等原始类型的值使用new命令,就会抛出这种错误,因为new命令    的参数应该是一个构造函数。

URIError: URIError是URI相关函数的参数不正确时抛出的错误,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、  escape()和unescape()这六个函数。

EvalError: eval函数没有被正确执行时,会抛出EvalError错误。该错误类型已经不再在ES5中出现了,只是为了保证与以前代码兼容,才继续保留。

以上这6种派生错误,连同原始的Error对象,都是构造函数。

2、系统错误,这类错误由底层系统触发,如试图打开不存在的文件,试图通过已关闭的Soket发送数据时等。系统错误是对JavaScript错误Error对象的一个扩展,它们表示程序能够处理的操作错误,这些错误信息都是在系统级别生成的。系统错误实例中除Error实例中的属性外,还包括以下几个属性:

       1、error.syscall - 一个表示失败的系统调用信息的字符串

       2、error.errno - 一个整数的错误码

       3、error.code - 表示错误字符串,通常是大写字母E开头

const os = require('os');

console.log(os.constants.errno);

常见的系统错误列表可以通过 Node.js 的 os 对象常看列表.

3、自定义错误,这类错误在应用代码中由用户指定触发

开发者可以使用它们,人为生成错误对象的实例。

new Error("出错了!"); 
new RangeError("出错了,变量超出有效范围!"); 
new TypeError("出错了,变量类型无效!");

上面代码表示新建错误对象的实例,实质就是手动抛出错误。可以看到,错误对象的构造函数接受一个参数,代表错误提示信息(message)。

你应该知道在 JavaScript 里,错误和异常是有区别的。错误是 Error 的一个实例。错误被创建并且直接传递给另一个函数或者被抛出。如果一个错误被抛出了那么它就变成了一个异常

二.nodejs中有三种基本的传递错误的模式Throw, Callback 和 EventEmitter

  • throw以同步的方式传递异常–也就是在函数被调用处的相同的上下文。如果调用者(或者调用者的调用者)用了try/catch,则异常可以捕获。如果所有的调用者都没有用,那么程序通常情况下会崩溃。
  • callback(err, data) 这种形式的错误处理起来繁琐, 并不具备强制性, 目前已经处于仅需要了解, 不推荐使用的情况
  • 通过 EventEmitter 的错误监听形式为各大关键的对象加上错误监听的回调. 例如监听 http server, tcp server 等对象的 error 事件以及 process 对象提供的 uncaughtException 和 unhandledRejection 等等.

那么,什么时候用throw,什么时候用callback,什么时候又用EventEmitter 呢?这取决于两件事:

  • 这是操作失败还是程序员的失误?
  • 这个函数本身是同步的还是异步的。

直到目前,最常见的例子是在异步函数里发生了操作失败。在大多数情况下,你需要写一个以回调函数作为参数的函数,然后你会把异常传递给这个回调函数。这种方式工作的很好,并且被广泛使用。例子可参照 NodeJS 的 fs 模块。如果你的场景比上面这个还复杂,那么你可能就得换用 EventEmitter 了,不过你也还是在用异步方式传递这个错误。

其次常见的一个例子是像JSON.parse 这样的函数同步产生了一个异常。对这些函数而言,如果遇到操作失败(比如无效输入),你得用同步的方式传递它。你可以抛出(更加常见)或者返回它。

对于给定的函数,如果有一个异步传递的异常,那么所有的异常都应该被异步传递。可能有这样的情况,请求一到来你就知道它会失败,并且知道不是因为程序员的失误。可能的情形是你缓存了返回给最近请求的错误。虽然你知道请求一定失败,但是你还是应该用异步的方式传递它。

通用的准则就是 你即可以同步传递错误(抛出),也可以异步传递错误(通过传给一个回调函数或者触发 EventEmitter 的 error事件),但是不用同时使用。以这种方式,用户处理异常的时候可以选择用回调函数还是用try/catch,但是不需要两种都用。具体用哪一个取决于异常是怎么传递的,这点得在文档里说明清楚。

三、错误处理的原则


1.try中尽量少的包含语句 
原因是因为,不管是try块中的代码还是catch块中的代码,都是无法让V8引擎进行任何优化的,不能优化的函数比优化的函数会慢上好几倍
首先来测试一下node中v8 对try catch 代码的执行速度
console.time(1)try {  for (vari =0;i < 1000000000;i++) {    var p =i %2  }  throw newError()} catch(e) {}console.timeEnd(1)function run() {  for (vari =0;i < 1000000000;i++) {    var p =i %2  }}// 取出来放在外面console.time(2)try {  run()  throw newError()} catch(e) {}console.timeEnd(2)

打印结果:

1: 5813.965ms

2: 944.025ms

足足差了6倍之多,所以最好让代码在try catch之外执行。

不过在 Node.js v7.6 之后使用了升级引擎的新版 v8, 旧版中 try/catch 代码不能优化的问题也解决了

2.如果已经提前预知错误的类型,就用if...else...来代替try...catch
除掉错误预知的这个前提下,if..else..的用法与try...catch...的用法是基本相同的,严格意义来说,if..else..的可读性比try..catch..更强,语句理解能力也更好,开发人员与用户更加倾向于使用if..else.. 但是如果无法判断语句是否正确的话,仍然使用try..catch...会更加保险

四. koa2中错误处理代码实例:

在koa等最新框架中可以更优雅的处理错误,koa使用黑魔法让我们可以用try catch来捕获异步代码中的错误。在koa中,推荐统一使用throw try catch 的方式来处理异常。koa中的异常可以集中处理,处理过程:首先我们新建一个error.js文件,代码如下:

module.exports =asyncfunction (ctx,next){  let timeStart = +new Date();  try{    awaitnext();// 所有在此之后使用的中间件的代码都被包在这个try里了,但是代码是在外面执行  } catch(err) {    letstatus =err.status ||'500';    if (err.code ==200) {      //logger.warn('Please do not put the error code to 200');    }    ctx.body = {      status:status,      message:err.message,      stack:err.stack, // 非生产环境打印错误堆栈,便于定位错误      name: err.name,    }     ctx.app.emit('error',err,this);// 被catch住的error不会触发app的error事件,需主动触发,在app中调用app.on('error')可以监听到   } finally {     //在finally中可以进行一些最后的处理,例如释放资源等,也可以简单计算出代码的执行时间,例如     let time_cost = +newDate() -timeStart; //此time_cost即为执行时间 }}
代码执行到await next()这句,会在此暂停,然后执行next方法,也就是调用下一个中间件,以此类推执行完所有中间件,这样如果我们把其余的中间件都写在这个error中间件之后,就相当于所有的中间件代码都被这个try块包住了,发生的错误都可以被catch到,然后我们在catch里统一错误格式,统一返回给调用方。

参考文章:

https://github.com/ElemeFE/node-interview/blob/master/sections/zh-cn/error.md
https://github.com/koajs/koa/wiki/Error-Handling


原创粉丝点击