5 node.js express async-await es6-promise express-4
这两个中间件功能表现不同,我无法弄清楚原因:
在这里,错误将被try/catch困住:
router.get('/force_async_error/0', async function (req, res, next) {
try{
await Promise.reject(new Error('my zoom 0'));
}
catch(err){
next(err);
}
});
Run Code Online (Sandbox Code Playgroud)
但是在这里,错误不会被try/catch困住:
router.get('/force_async_error/1', async function (req, res, next) {
await Promise.reject(new Error('my zoom 1'));
});
Run Code Online (Sandbox Code Playgroud)
我认为Express用try/catch包装了所有中间件函数,所以我看不出它会有什么不同的行为?
我查看了Express源代码,处理程序看起来像:
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next); // shouldn't this trap the async/await error?
} catch (err) {
next(err);
}
};
Run Code Online (Sandbox Code Playgroud)
那么为什么try/catch没有捕获抛出的错误?
我将在这里添加一个答案,即使你已经接受了另一个,因为我认为这里发生的事情可以更好地解释,这将有助于其他人试图理解这一点.
在你的代码中:
router.get('/force_async_error/1', async function (req, res, next) {
await Promise.reject(new Error('my zoom 1'));
});
Run Code Online (Sandbox Code Playgroud)
我们来讨论发生了什么:
首先,您声明了async为了在其中使用而必须执行的回调await.一个async函数告诉解释器做几件重要的事情.
1.异步函数总是返回一个promise. promise的解析值将是函数返回的任何值.
2.异步函数内部包含一个try/catch. 如果在函数代码的顶级范围中抛出任何异常,那么将捕获这些异常并自动拒绝该函数返回的promise.
3.异步功能允许您使用await. 这是解释器的一个指示器,它应该实现并允许await函数内部的语法.这与上面的前两点有关,这就是为什么你不能await在任何'ol函数中使用它.任何未被捕获的拒绝await也将拒绝该函数返回的承诺.
重要的是要理解虽然async/await语法允许你使用异常编程和try/catch之类的同步代码,但它并不完全相同.该函数仍然立即返回一个promise,函数中未捕获的异常会导致promise在以后的某个时间被拒绝.它们不会导致同步异常冒泡到调用者.因此,Express try/catch不会看到同步异常.
但是在这里,错误不会被try/catch困住
我认为Express用try/catch包装了所有中间件函数,所以我看不出它会有什么不同的行为?
那么为什么try/catch [在Express]中没有捕获抛出的错误?
这有两个原因:
被拒绝的承诺不是同步投掷,因此Express无法通过try/catch来捕获它.该函数只返回被拒绝的承诺.
Express根本没有查看路由处理程序回调的返回值(您可以在显示的Express代码中看到).因此,您的async函数返回一个稍后被拒绝的承诺的事实被Express完全忽略了.它只是这样做fn(req, res, next);,并没有注意返回的承诺.因此,对承诺的拒绝就被置若罔闻.
有一个名为Koa的类似Express的框架,它使用了许多承诺,并且注意到了返回的承诺,并且会看到你被拒绝的承诺.但是,这不是Express所做的.
如果你想在Express中使用一些Koa类型的功能,你可以自己实现它.为了保持其他功能不受干扰,以便它可以正常工作,我将实现一个名为的新方法getAsync,它确实使用了promises:
router.getAsync = function(...args) {
let fn = args.pop();
// replace route with our own route wrapper
args.push(function(req, res, next) {
let p = fn(req, res, next);
// if it looks like a promise was returned here
if (p && typeof p.catch === "function") {
p.catch(err => {
next(err);
});
}
});
return router.get(...args);
}
Run Code Online (Sandbox Code Playgroud)
然后你可以这样做:
router.getAsync('/force_async_error/1', async function (req, res, next) {
await Promise.reject(new Error('my zoom 1'));
});
Run Code Online (Sandbox Code Playgroud)
并且,它会适当地调用next(err)您的错误.
或者,您的代码甚至可能只是这样:
router.getAsync('/force_async_error/1', function (req, res, next) {
return Promise.reject(new Error('my zoom 1'));
});
Run Code Online (Sandbox Code Playgroud)
PS在一个完整的实现中,您可能会制作一堆动词的异步版本,并且您将为中间件实现它,并且您将它放在路由器原型上.但是,这个例子是为了告诉你如何工作,而不是在这里做一个完整的实现.
这是因为调用是异步的,采用以下代码:
try {
console.log('Before setTimeout')
setTimeout(() => {
throw new Error('Oups')
})
console.log('After setTimeout')
}
catch(err) {
console.log('Caught', err)
}
console.log("Point of non-return, I can't handle anything anymore")Run Code Online (Sandbox Code Playgroud)
如果运行它,您应该会看到错误是在 后触发的Point of non-return。当我们到达队列时throw已经太晚了,我们已经在try/之外了catch。此时如果抛出错误,它将被捕获。
您可以通过在调用者中async使用/来解决此问题(对被调用者来说无关紧要),即:await
void async function () {
try {
console.log('Before setTimeout')
await new Promise((resolve, reject) =>
setTimeout(() => {
reject(new Error('Oups'))
})
)
console.log('After setTimeout')
}
catch(err) {
console.log('Caught', err.stack)
}
console.log("Point of non-return, I can't handle anything anymore")
}()Run Code Online (Sandbox Code Playgroud)
最后,这意味着要让 Express 处理异步错误,您需要将代码更改为:
async function handle(req, res, next) {
// [...]
try {
await fn(req, res, next); // shouldn't this trap the async/await error?
} catch (err) {
next(err);
}
}
Run Code Online (Sandbox Code Playgroud)
更好的解决方法:
定义一个wrap这样的函数:
const wrap = fn => (...args) => Promise
.resolve(fn(...args))
.catch(args[2])
Run Code Online (Sandbox Code Playgroud)
并像这样使用它:
app.get('/', wrap(async () => {
await Promise.reject('It crashes!')
}))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1294 次 |
| 最近记录: |