Node.js 中的异样

Node.js 跟 JavaScript一样,同步代码中的异样咱们能够通过 try catch 来捕捉.

异步回调异样

但异步代码呢? 咱们来看一个 http server 启动的代码,这个也是个典型的异步代码。

const http = require('http')try {    const server = http.createServer(function (req, res) {        console.log('来了')        throw new Error('hi')        res.end('helo')    })    server.listen(3002)}catch (err) {    console.log('出错了')}

咱们发现异步代码的异样无奈间接捕捉。这会导致 Node.js 过程退出。最显著的就是 web server 间接挂掉了。

异步代码也有解决办法,咱们间接把try catch 写在异步代码的回调外面:

const http = require('http')try {    const server = http.createServer(function (req, res) {        try {            throw new Error('hi')        }        catch (err) {            console.log('出错了')        }        res.end('helo')    })    server.listen(3002)}catch (err) {    console.log('出错了')}

这样也能catch到谬误。

然而业务代码非常复杂,并不是所有的状况咱们都能预料到。比方在try...catch之后又呈现一个throw Error.

所有没有catch 的Error都会往上冒泡直到变成一个全局的 uncaughtException。 Node.js里对未捕捉的异样会查看有没有监听该事件,如果没有就把过程退出:

function _MyFatalException(err){    if(!process.emit('uncaughtException',err)){        console.error(err.stack);        process.emit('exit',1);    }}

因而,避免异步回调异样导致过程退出的方法好像就是监听该事件

process.on('uncaughtException', function(err) {    console.log('出错了,我记录你,并吃掉你')})const http = require('http')try {    const server = http.createServer(function (req, res) {        try {            throw new Error('hi')        }        catch (err) {            console.log('出错了')        }        throw new Error('有一个error')        res.end('helo')    })    server.listen(3002)}catch (err) {    console.log('出错了')}

这样过程不会退出。但 极其不优雅 。 因为 uncaughtException 中没有了req和res上下文,无奈敌对响应用户。另外可能造成内存透露(具体参考网络其余材料)

因而,uncaughtException 适宜用来做Node.js 整个利用最初的兜底。(记录日志or重启服务)

Promise的reject异样

如果应用了promise,且非异步reject了。在 Node.js 中,这个promise reject 行为会在控制台打印,但目前Node版本不会造成过程退出,也不会触发全局 uncaughtException.

promise最有争议的中央就是当一个promise失败然而没有rejection handler处理错误时静默失败。不过浏览器和Node.js都有相应的解决机制,两者大同小异,都是通过事件的形式监听. 有两个全局事件能够用来监听 Promise 异样:
  • unhandledRejection:当promise失败(rejected),但又没有解决时触发,event handler 有2个参数: reason,promise;
  • rejectionHandled: 当promise失败(rejected),被解决时触发,hanler 有1个参数: promise;

到底该如何解决异样

最好的解决形式,就是应该感知到本人业务代码中的异样。这样的话,无论业务开发人员本人解决了还是没解决,都能在利用下层catch到进行日志记录。 更佳的状况是:在感知到谬误后,能给浏览器一些默认的提醒。

可是业务代码里有同步有异步,如此简单的代码如何能全副cover住呢?

这个会有一些技巧:比方假如咱们的业务代码全副被包裹在本人的一个Promise中,且业务代码的每一个异步函数都能够被咱们注入catch回调。在这样完满的状况下,咱们就能在最外层捕捉外部产生的所有异样了。

Koa 就是这么干的。Koa1 用 co来运行中间件,co就能够把generator运行起来且捕捉其中的异步谬误。想理解具体原理的,可能要去看更具体的材料了

Koa 中捕捉异样和谬误的机制

  • 业务本人try catch

这种形式任何JavaScript程序都能够应用,是业务开发人员本人要做的。不多说了

  • 写前置中间件

因为Koa是洋葱模型,因而能够在业务逻辑的前置中间件里捕捉前面中间件的谬误。这里是基于 yield 异步异样能够被try catch的机制。例如:

app.use(function *(next) {  try {    yield next;  } catch (err) {    console.log('哇哈 抓到一个谬误')    // 敌对显示给浏览器    this.status = err.status || 500;    this.body = err.message;    this.app.emit('error', err, this);  }});

实际上,上述中间件的工作 ctx.onerror 曾经做了。 Koa 内核会主动把中间件的谬误交给 ctx.onerror 解决,因而这个中间件我感觉没必要写了(除非要自定义这个默认的错误处理逻辑)。

  • 监听app.on('error')

如果所有中间件都没有捕捉到某个异样,那么co会捕捉到。co会调用context对象的onerror, 从而做一些解决(例如返回给浏览器500谬误),同时触发 app.onerror

因而,在app.onerror里,你能够做些日志记录或自定义响应

  • uncaughtException

如果 Koa 都没有捕捉到异样,那么就由Node来兜底了。不过这个个别不会产生,除非你在app.onerror里还要扔出异样(然而这是个promise异样,也不会触发uncaughtException)。

Koa错误处理最佳实际

  • 抛出异样

在 Koa1 中间件里,你能够应用 this.throw(status, msg) 抛出异样。 Koa的底层其实实质上会应用 http-errors模块包装这个Error, 并间接 throw这个异样。

以下是 this.throw 函数源码:

  // 将你传递的错误码和msg包装为一个 Error对象  throw: function(){    throw createError.apply(null, arguments);  }

其中 createError函数相当于:

var err = new Error(msg);err.status = status;throw err; // 包装后再抛出,ctx.onerror能力正确响应错误码给浏览器,否则都是500

因而 中间件 中你调用 this.throw 函数实际上就是真的 throw了一个异样,最终会导致 co 异样。

因为前文讲到的 Koa co 谬误捕捉机制(co-->catch-->ctx.onerror-->app.onerror),因而,你在任何中间件中throw的异样都能够被app.onerror捕捉到。

  • 逃逸的异样

co在运行generator时,如果某个yield右侧又是一个generator,那么co也会递归地去运行它。当然也会捕捉这个嵌套的异步异样。但有些状况下嵌套异步会逃出一个异步的谬误检测机制。

比方在Promise里做了另外一个异步操作, 在另外的异步操作里抛出了异样。

var fn = function () {    return new Promise (function (resolve, reject) {        setTimeout(function(){throw new Error('inner bad')})    })}

这个异样,Promise就无奈catch到。 同样,在generator里如果用了这样的形式,异样也会逃逸导致无奈捕捉。

问题:逃逸出Koa 的 co异步调用链的代码,会导致co无奈catch异样。

不如去看看egg怎么做的吧