在express
中时使用 Async/await
编写异步代码时,每个 async 函数都要包裹在try/catch
中,代码量多了看着冗余不优雅,express
又不像koa
的异步机制可以订阅全局的error
事件,为了解决这个问题,需要写个捕获异步函数异常的中间件。
uncaughtException
开始能想到的肯定是try/catch
了,但是也想过能否使用nodejs
提供的uncaughtException
事件,在全局捕获异常,例如下面的代码:
process.on("uncaughtException", (err) => console.log("uncaught Exception"));
const asyncError=()=>{
throw new Error("some Error");
}
asyncError();
asyncError
方法里面抛出的异常会被 uncaughtException
订阅,但是在异步函数中,并没走到 uncaughtException
,还是会抛出异常:
process.on("uncaughtException", (err) => console.log("uncaught Exception"));
const asyncError=()=>{
throw new Error("some Error");
}
(async ()=>{
// 抛出异常
asyncError();
})()
而且Promise.reject
也没走到uncaughtException
里面:
const asyncError=()=>{
return Promise.reject("some error")
}
(async ()=>{
// 抛出异常
await asyncError();
})()
所以在express
中使用nodejs提供的uncaughtException
处理异步错误不太合适,一方面没法捕获和定位上下文错误,另一方面也没法将错误异常提供给中间件函数处理
解决思路
要处理express
中的异步函数错误,最好的方法当然是编写处理异常的中间件了,try/catch
开路,包裹中间件方法,catch
到的异常直接交给next
函数处理,代码如下:
const asyncHandler = fn =>{
return (req,res,next)=>{
try{
fn(req,res,next)
}catch(next)
}
}
module.exports = asyncHandler;
接下来,在异步函数中引入中间件处理:
app.use(asyncHandler(async(req, res, next) => {
await authenticate(req);
next();
}));
app.get('/async', asyncHandler(async(req, res) => {
const result = await request('http://example.com');
res.end(result);
}));
使用asyncHandler
方法包裹的async/await
函数,如果出现错误就会被Error-handling
中间件捕获了
但是每次用到异步函数的时候都要包裹asyncHandler
方法,真正用起来也不是很爽,这里推荐使用express-async-errors中间件,其原理是将express
里面的中间全部包裹上一层asyncHandler
方法,让错误异常无所遁形,全部跑到Error-handling
中间件。
前提是引入express
后,先引入express-async-errors
方法:
const express = require('express');
require('express-async-errors');
const User = require('./models/user');
const app = express();
app.get('/users', async (req, res) => {
const users = await User.findAll();
res.send(users);
});
接下来的在异步函数中,就不用都包裹上try/catch
了,有错误提前throw Error
,写起代码来美滋滋:
app.use(async (req, res) => {
const user = await User.findByToken(req.get('authorization'));
if (!user) throw Error("access denied");
});
app.use((err, req, res, next) => {
if (err.message === 'access denied') {
res.status(403);
res.json({ error: err.message });
}
next(err);
});~~~~
参考链接:
Using Async/await in Express
Handling errors in express async middleware