快速错误处理和异步等待
Posted
技术标签:
【中文标题】快速错误处理和异步等待【英文标题】:Express error handling and async await 【发布时间】:2017-05-25 00:19:26 【问题描述】:在我的 Node.js 应用程序中,我添加了以下代码来捕获每个未捕获的异常:
process.on('uncaughtException', function (err: Error)
try
logger.err(err);
catch (err)
);
问题在于 Express 有自己的默认错误处理程序,它会捕获每个未捕获的异常。现在,Express 在 Node (process.on) 之前捕获了异常,所以我的记录器没有被访问。但是,可以添加另一个可以在 Express 之前捕获每个异常的错误处理程序:
app.use(logErrors);
function logErrors (err: Error, req: Request, res: Response, next: NextFunction)
logger.err(err);
next(err);
这仍然不能涵盖所有情况。每当我有一个用await
调用的async function
时,也不例外,但会返回一个被拒绝的Promise。例如:
app.get('/foo', async function (req: Request, res: Response, next: NextFunction)
await bar();
);
function bar()
throw new Exception();
不会到达我的 logErrors
函数,因为它不会抛出,但会返回一个被拒绝的 Promise。
为了解决这个问题,我用另一个函数包装了我的 Express HTTP 处理程序:
app.get('/foo', wrap(async function (req: Request, res: Response, next: NextFunction)
await bar();
));
function wrap(func: (req: Request, res: Response, next: NextFunction) => void)
return async function (req: Request, res: Response, next: NextFunction)
try
await func(req, res, next);
catch (err)
next(err);
next(err) 将错误传递给我的处理程序。现在,我设法捕获了 logErrors
函数的异常。
我快完成了。我仍然有一种情况无法发现错误。当我在没有 await
关键字的情况下调用 async function
时会发生这种情况(有时在代码中的两个不同位置使用相同的函数很有用,一次是异步调用,一次是同步调用)。所以这段代码不会捕捉到错误:
app.get('/foo', wrap(async function (req: Request, res: Response, next: NextFunction)
bar();
));
这里发生的是 Express HTTP 处理程序将已解析的 Promise 返回给 wrap
函数。 wrap 函数又不会到达 catch
块,因此它不会调用 next(err)
会到达我的记录器。
bar
函数依次返回一个被拒绝的 Promise,但没有人在等待它的返回值。
我怎样才能更改我的代码,以免最终导致任何未处理的 Promise 拒绝? (仅通用解决方案)
【问题讨论】:
我认为同步调用异步函数是个坏主意,实际上不可能。您可以忽略该函数是异步的事实并调用它,但它仍将异步运行,您只会遇到您描述的问题。所以我的建议是不要以同步方式使用异步函数——它们是完全不同的野兽。 @Amid 他们并没有真正同步。我的意思是:当我在没有“等待”的情况下调用它时是异步的。当我用'await'调用它时是同步的(不是真的,但有点:代码是逐行执行的,但是每次我在后台调用promise时线程都可以处理其他请求)。 这就是我的意思。通过在没有等待的情况下调用异步函数,您会遇到此类问题。它们实际上不应该以这种方式调用,而您的示例中的异常实际上不是异常,但承诺拒绝是一个很好的说明。因此,即使它们是某个次要事件处理程序的最后一行,我也不建议这样做——使用 await 调用它们仍然是一个好习惯。 @Amid 你是对的,我同意你的观点,我总是用'await'调用异步函数,即使它是任务的最后一行。我这样做是因为我想在不让他等待的情况下向客户返回响应。我还能怎么做?我听说调用 res.end() 不是一个好主意,因为它会跳过应该执行的中间件。 我想你必须选择这里。要么您将执行返回给客户端,它不会获得有关错误/异常的任何信息。或者您等待异步操作完成并获得所有可用信息。 【参考方案1】:还有另一个process.on
事件,您可以为其设置侦听器 - unhandledRejection。
您可以使用它来处理所有代码中的这些拒绝。
注意:记得在记录完所有需要的内容后终止进程。更多关于这个here。
【讨论】:
【参考方案2】:我找到了解决办法:
app.get('/foo', async function (req: Request, res: Response, next: NextFunction)
dontAwait(() => bar());
);
async function dontAwait(func: () => void)
try
await func();
catch (err)
logErrors(err);
【讨论】:
【参考方案3】:我仍然有一种情况无法捕捉到错误。
你可以,但你目前根本不对结果做任何事情。如果您希望触发通常的错误处理程序,您需要到await
它。
当我调用没有
await
关键字的async function
时会发生这种情况
我看不出有什么好的理由。但是如果你真的想fire and forget the function,你可以做到。你只需要明确地处理错误:
bar().catch(e => logger.err(e));
【讨论】:
感谢您的回复。你的建议比我的好,但我还是更喜欢基里尔的建议。顺便说一句,我想在没有 await 关键字的情况下调用异步函数是有充分理由的。您可以在问题下方的 cmets 中看到我与 Amid 的通信。 OK,“我想在不让他等待的情况下向客户返回响应。”是一个合理的用例,但很少见。但是,我不认为这是“一劳永逸”的情况,因为您可能想对调用的结果(成功或失败)做一些事情:存储它并通知用户您已完成。unhandledRejection
跟踪不是一个好的解决方案,因为它确实松散了上下文。是的,记录它们是个好主意,但你不应该期待它们。
Bergi 你是对的,unhandledRejection 通常只对记录有用。当用户需要启动一个他不应该在 HTTP 响应中得到结果的后台操作时,调用不带“等待”的函数很有用。他可以稍后通过电子邮件或彗星/投票获得结果。在这个过程中,我会处理我能想到的每一种失败。这种通用错误处理只应该记录我没想到的故障。在这种情况下,我将不得不按照基里尔的说法重新启动我的应用程序,最后我必须修复我的代码以处理这些故障。
是的,对于该用例,请使用 unhandledRejection
s。让我失望的是你想调用默认的 Express 错误处理程序,所以我认为你仍然需要一些特定于请求的错误处理。以上是关于快速错误处理和异步等待的主要内容,如果未能解决你的问题,请参考以下文章