摘要
-
比起回调函数,使用 Promise 来处理异步错误要显得优雅许多。
-
结合 Express 内置的错误处理机制和 Promise 极大地降低产生未捕获错误(uncaught exception)的可能性。
-
Promise 在ES6中是默认选项。如果使用 Babel 转译,它也可以与 Generators 或者 Async/Await 相结合。
本文主要阐述如何在 Express 中使用错误处理中间件(error-handling middleware)来高效处理异步错误。在 Github 上有对应代码实例可供参考。
首先,让我们一起了解 Express 提供的开箱即用的错误处理工具。然后,我们将探讨如何使用 Promise, Generators 以及 ES7 的 async/await 来简化错误处理流程。
Express 内置的异步错误处理
在默认情况下,Express 会捕获所有在路由处理函数中的抛出的异常,然后将它传给下一个错误处理中间件:
app.get('/', function (req, res) {
throw new Error('oh no!')
})
app.use(function (err, req, res, next) {
console.log(err.message) // 噢!不!
})复制代码
对于同步执行的代码,以上的处理已经足够简单。然而,当异步程序在执行时抛出异常的情况,Express 就无能为力。原因在于当你的程序开始执行回调函数时,它原来的栈信息已经丢失。
app.get('/', function (req, res) {
queryDb(function (er, data) {
if (er) throw er
})
})
app.use(function (err, req, res, next) {
// 这里拿不到错误信息
})复制代码
对于这种情况,可以使用 next 函数来将错误传递给下一个错误处理中间件
app.get('/', function (req, res, next) {
queryDb(function (err, data) {
if (err) return next(err)
// 处理数据
makeCsv(data, function (err, csv) {
if (err) return next(err)
// 处理 csv
})
})
})
app.use(function (err, req, res, next) {
// 处理错误
})复制代码
使用这种方法虽然一时爽,却带来了两个问题:
-
你需要显式地在错误处理中间件中分别处理不同的异常。
-
一些隐式异常并没有被处理(如尝试获取一个对象并不存在的属性)
利用 Promise 传递异步错误
在异步执行的程序中使用 Promise 处理任何显式或隐式的异常情况,只需要在 Promise 链尾加上.catch(next) 即可。
app.get('/', function (req, res, next) {
// do some sync stuff
queryDb()
.then(function (data) {
// 处理数据
return makeCsv(data)
})
.then(function (csv) {
// 处理 csv
})
.catch(next)
})
app.use(function (err, req, res, next) {
// 处理错误
})复制代码
现在,所有异步和同步程序都将被传递到错误处理中间件。棒棒的。
虽然 Promise 让异步错误的传递变得容易,但这样的代码仍然有一些冗长和刻板。这时候 promise generator 就派上了用场。
用 Generators 简化代码
如果你使用的环境原生支持 Generators,你可以手动实现以下的功能。不过这里我们将借用 Bluebird.coroutine 来说明如何使用 Promise generator 来简化刚才的代码。
尽管接下来的例子使用的是 bluebird ,其它 Promise 库(如 co)也都支持 Promise generator.
首先,我们需要使得 Express 路由函数与 Promise generator 兼容:
var Promise = require('bluebird')
function wrap (genFn) { // 1
var cr = Promise.coroutine(genFn) // 2
return function (req, res, next) { // 3
cr(req, res, next).catch(next) // 4
}
}复制代码
这个函数是一个高阶函数,它做了以下几件事情:(分别与代码片段中的注释对应)
-
以 Genrator 为唯一的输入
-
让这个函数懂得如何 yield promise
-
返回一个普通的 Express 路由函数
-
当这个函数被执行时,它会使用 coroutine 来 yield promise,捕获期间发生的异常,然后将其传递给 next 函数
借助这个函数,我们就可以这样构造路由函数:
app.get('/', wrap(function *(req, res) {
var data = yield queryDb()
// 处理数据
var csv = yield makeCsv(data)
// 处理 csv
}))
app.use(function (err, req, res, next) {
// 处理错误
})复制代码
现在,Express 的异步错误处理流程的可读性已经近乎令人满意,而且你可以像写同步执行的代码一样去书写异步执行的代码,唯一不要忘了的就是 yield promises。
然而这还不是终点,ES7 的 async/await 提议可以让代码变得更简洁。
使用 ES7 async/await
ES7 async/await 的行为就像 Promise Generator 一样,只不过它可以被用到更多的地方(如类方法或者胖箭头函数)。
为了在 Express 中使用 async/await,同时优雅地处理异步错误,我们仍然需要一个与上文提到的wrap 类似的函数:
let wrap = fn => (...args) => fn(...args).catch(args[2])复制代码
这样,我们就可以按底下这种方式书写路由函数:
app.get('/', wrap(async function (req, res) {
let data = await queryDb()
// 处理数据
let csv = await makeCsv(data)
// 处理 csv
}))复制代码
现在可以愉快地写代码了
有了对同步和异步错误的处理,你可以用新的方式来开发 Express App。但有两点需要注意:
- 要习惯使用 throw ,它使得你的代码目的明确,throw 会明确地将程序引到错误处理中间件,这对同步或异步的程序都是适用的。
- 遇到特殊情况,当你觉得有必要时,也可以自行 try/catch。
app.get('/', wrap(async (req, res) => {
if (!req.params.id) {
throw new BadRequestError('Missing Id')
}
let companyLogo
try {
companyLogo = await getBase64Logo(req.params.id)
} catch (err) {
console.error(err)
companyLogo = genericBase64Logo
}
}))复制代码
- 要习惯使用 custom error classes,如 BadRequestError,因为这可以让你在错误处理中间件中更方便地分类处理。
app.use(function (err, req, res, next) {
if (err instanceof BadRequestError) {
res.status(400)
return res.send(err.message)
}
...
})复制代码
需要注意
- 以上介绍的方法要求所有异步操作必须返回 promise。如果你的异步操作是使用回调函数的方式,你需要将其转化成 promise。(可以直接使用 Bluebird.promisifyAll 这类函数)
-
事件发射器(如 steams)仍然会导致未捕获异常,你需要注意合理地处理这类情况:
相关推荐
主要介绍了node.js express捕获全局异常的三种方法,结合实例形式简单分析了node.js express捕获全局异常的常见操作方法与使用注意事项,需要的朋友可以参考下
Express 的异步错误处理程序 安装 npm install express-domain 用法 var express = require ( 'express' ) , expressDomain = require ( 'express-domain' ) ; var app = express ( ) . use ( expressDomain ( )...
简单的中间件,用于处理异步Express路由内的异常并将它们传递给您的Express错误处理程序。 安装: npm install --save express-async-handler 或者 yarn add express-async-handler 用法: const asyncHandler ...
本文实例讲述了Nodejs异步回调之异常处理。分享给大家供大家参考,具体如下: 目前我们项目的Nodejs异常是通过express next 到 errorhandler 中间件去处理的, 原本以为此方法可以捕获到所有的异常,但事实发现并非...
遇到异常后,该中间件将停止接受新请求,等待用户的异常处理程序和所有当前正在运行的请求在某个宽限期内完成并退出。安装$ npm install express-graceful-shutdown-handler原料药 var gracefulShutdown = require ...
Express.js异步中间件包装器,用于处理被拒绝的承诺和同步异常。 该库与相似,但是是用TypeScript编写的,其类型处理的情况比express-async-handler (例如,它可以处理引入的更改)。 用法 import { Request , ...
本文主要阐述如何在 Express 中使用错误处理中间件(error-handling middleware)来高效处理异步错误。在 Github 上有对应 代码实例 可供参考。
由于未在 Windows 中完全配置 Microsoft 分布式事务处理协调器 (MS DTC),因此,应用程序可能无法在分布式事务中登记 SQL Server Express 资源。此问题会影响使用分布式事务的链接服务器、分布式查询以及远程存储...
此代码是从借用的,用于处理Express中的async / await路由。 此npm模块允许您在打字稿源中使用此解决方案。 安装 # With npm npm install --save async-wrapper-express-ts # With yarn yarn add async-wrapper-...
第 1 章 初识 Express .......................................................................................................................1 1.1 JavaScript 革命 .........................................
说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。 异常详细信息: System.Data.SqlClient.SqlException: 用户 'sa' 登录失败。该用户与...
示例节点(Express + Mongoose)代码库,包含遵循 API规范的真实示例(CRUD,身份验证,... Mongoose仅在文档级别处理验证,因此整个集合中的唯一索引将在驱动程序级别引发异常。 mongoose-unique-validator插件可以
异步异常处理 异步异常的特点 由于node的回调异步特性,无法通过try catch来捕捉所有的异常: try { process.nextTick(function () { foo.bar(); }); } catch (err) { //can not catch it } 而对于web服务而言...
redux,express,koa中间件实现对比分析这里是示例代码,简单模拟了redux,express,koa的执行流程,替换出了中间件的核心实现,应保留原始结构和主流程,去掉异常处理,替换校准,辅助工具等干扰代码,更方便的帮助...
全局异常处理 通过创建单个文件来创建端点 与mongo的连接 用于显示错误和成功响应的中间件 使用的图书馆 该模板使用以下库: 处理环境变量: 记录器: 一般的: 猫鼬 安装 必须安装该节点。 此外,要在本地使用...
一个快速的程序包,使您可以轻松处理API问题。 它提供了的直接实现,并将您引发的异常以以下格式返回,且内容类型的标头设置为application/problem+json { " status " : 403 , " type " : " ...
如果你向它传递一个返回承诺的函数,它会调用.then()方法来为你处理异常。 您还可以将您的处理程序绑定到您选择的对象。例子 // wrap a routervar expressRouter = express . Router ( ) ;var router = new Router ...
我重写并改进的Node.js的Connect框架...* 注意到示例中远程联接timeout会抛出异常终止服务器,尽管代码中已经添加了错误处理。Node.js的net模块在延时回调中抛出异常(!),代码也不易理解,因此难以跟踪判断错误位置。
第7章 结构化异常处理 174 7.1 错误、bug与异常 174 7.2 NET异常处理的作用 174 7.3 最简单的例子 176 7.4 配置异常的状态 180 7.5 系统级异常(System.SystemException) 183 7.6 应用程序级异常(System...
诺言路由器 yarn add @tgriesser/promise-routerExpress Router,在任何返回承诺的路由上附加一个.catch(next) ,以确保未处理的异常不会挂起该进程。 允许在快速中间件中使用异步/等待,而无需担心用: import { ...