为什么有时异步代码中的错误会使node.js服务器崩溃

时间:2019-04-10 17:08:48

标签: javascript node.js asynchronous error-handling crash

我在某些网站上读到了express.js中的“异步代码中任何未捕获的错误都可能导致HTTP服务器崩溃而导致DoS”。 我已经通过本示例进行了检查,但是我想知道为什么如果在Express回调内发生错误,服务器不会崩溃,但是如果在setTimeout()函数内部发生,则服务器崩溃。

两个示例中的错误均不是在异步代码中发生的吗?还是其中之一不是异步的而我弄错了? 为什么某些异步代码中未捕获的错误会使服务器崩溃,而其他异步代码中却没有崩溃?

var express = require("express");

var app = express();

http: app.get("/e1", (req, res, next) => {
  let p = req.query.p;
  let pn = parseInt(p, 10);

  //If the error happens here the server does not crashes
  let s = pn + y; // y does not exist, so an error occurs

  res.send("hi");
});

http: app.get("/e2", (req, res, next) => {
  let p = req.query.p;
  let pn = parseInt(p, 10);

  setTimeout(() => {
    //If the error happens here the server crashes
    let s = pn + y; // y does not exist, so an error occurs
  }, 100);

  res.send("hi");
});

app.listen(3000, function() {
  console.log("Example app listening on port 3000!");
});

1 个答案:

答案 0 :(得分:0)

如果我们想到throwcatch堆栈上运行,可能会变得很清楚:

throw:在堆栈中向下移动,直到找到处理程序,然后从那里继续。

catch将错误处理程序添加到堆栈中。

对于同步代码,可以将其可视化为:

 // Legend:
 -> function call
 <- function returns

 http.request -> express.handler -> [try] -> your function -> nested call -> Throw!
 <-            <-                   [catch] <-----------------------------------

现在,当您启动异步操作时,某个回调将在某个时候被回调,并且该回调将最终在新的堆栈上:

 // the current stack:
 http.request -> express.handler -> [try] -> your function -> start async action
 <-            <-                 <-       <-             <-

 // somewhen later, the timeout calls back
 timer -> your setTimeout callback -> nested call -> Throw!
 Crash! <-----------------------------------------------------

现在,由于Express将catch处理程序附加到回调中的代码中:

 Express.get = function(callback) {
   //...
   try {
     callback(req, res, next);
   } catch(error) {
    // handle error
   }
 };

将处理错误,但仅处理同步错误(简化后,实际代码为here


现在该怎么办?

基本上:将每个回调都包装到一个promise中(这样可以简化异步错误处理):

 const delay = ms => new Promise(res => setTimeout(res, ms));

然后await创建的每个承诺,然后将所有内容包装在try / catch中:

 app.get(async (req, res) => {
   try {
      await delay(2000);
      const p = q + d;
   } catch(error) {
     res.status(500).send("whoops");
  }
});

之所以可行,是因为await会“保持堆栈”(但只是async的一个函数在嵌套调用中await,这就是我们需要添加自己的{{1} } / try,因为Express不会在回调调用上catch),因此嵌套await ed函数之一内部的错误将退回到我们的错误处理程序。

await