Koa入门教程[3]-错误和异常处理

Node.js 中的异常

Node.js 跟 JavaScript同样,同步代码中的异常咱们能够经过 try catch 来捕获.html

异步回调异常

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

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 直接挂掉了。promise

异步代码也有解决办法,咱们直接把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. app

全部没有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上下文,没法友好响应用户。另外可能形成内存泄漏(具体参考网络其余资料)ui

所以,uncaughtException 适合用来作Node.js 整个应用最后的兜底。(记录日志or重启服务)this

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怎么作的吧

相关文章
相关标签/搜索