乐趣区

关于koa:Koa入门教程3错误和异常处理

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

退出移动版