为啥我在 Nodejs 中收到“无法在将标头发送到客户端后设置标头”错误?

Posted

技术标签:

【中文标题】为啥我在 Nodejs 中收到“无法在将标头发送到客户端后设置标头”错误?【英文标题】:Why am I getting "Cannot set headers after they are sent to the client" error in Nodejs?为什么我在 Nodejs 中收到“无法在将标头发送到客户端后设置标头”错误? 【发布时间】:2021-06-14 11:53:21 【问题描述】:

我在 nodejs 中收到 Cannot set headers after they are sent to the client 错误,我不知道为什么。代码如下:我正在使用 mongoose 在 mongodb 中保存数据。我正在尝试保存数据并检查特定条件并返回因此,但错误一直存在。

const trendingposts = (req, res) => 
  Post.find(, function (err, docs) 
    if (docs.length == 0) 
       return res.send(message:"No posts");
    
    docs.forEach(function (data) 
      var id = data.id;
      var likes = data.likes.length;

      var comments = data.comments.length;
      const date2 = new Date();
      const diffTime = Math.abs(date2 - data.created);
      const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
      Post.findByIdAndUpdate(
        id,
         $inc:  score: (likes * comments) / diffDays  ,
        function (errr, doc) 
          if (errr) 
            console.log(err);
          
        
      );
    );
  ).exec((err, posts) => 
    if (err) 
      console.log(err);
    
  );
  var mysort =  score: -1 ;
  Post.find()
    .populate("postedBy")
    .populate("comments.postedBy")
    .populate("comments.incomments.postedBy")
    .populate("comments.likes")
    .sort(mysort)
    .limit(10)
    .exec((er, result) => 
      
      res.json(result);
    );
;

【问题讨论】:

你的代码有太多不必要的绒毛。将其简化为仅包含错误部分的最小示例。 【参考方案1】:

就在这里:

Post.find(, function(err, docs) 
    if (docs.length == 0)
        return res.send( message: "No posts" );

如果您达到docs.length == 0 的条件,那么您将发送对请求的响应。但是,您的 return 仅从 Post.find() 回调返回。它不会从您的 trendingposts() 函数返回。

因此,与此同时,该函数继续执行并最终到达此代码:

var mysort =  score: -1 ;
Post.find()
    .populate("postedBy")
    .populate("comments.postedBy")
    .populate("comments.incomments.postedBy")
    .populate("comments.likes")
    .sort(mysort)
    .limit(10)
    .exec((er, result) => 

        res.json(result);
    );

然后您向同一请求发送另一个响应。这就是触发您看到的错误Cannot set headers after they are sent to the client 的原因。


有许多不同的方法可以防止这种情况,但它们可能都与您通常如何清理此功能有关。按照现在的编写方式,您基本上启动了两个完全独立的异步代码路径。两者都以Post.find() 开头并从那里开始。它们每个都并行运行,并且都不知道其他代码路径在做什么。因此,您没有具体的方法来发送一个响应,但不能同时发送两个响应。

因此,清理此问题的方法可能是不具有两个完全独立的异步代码路径。你需要以某种方式协调它们。在几乎所有情况下,您都希望切换到数据库的承诺接口,因为这将为您提供更多管理控制流的选项。例如,如果出于性能原因,您希望同时进行两个并行异步操作,并使用 Promise,您可以使用 Promise.all()Promise.allSettled() 来监控两者并知道它们何时完成,然后掌握两个结果,决定发送哪个响应。

或者,如果您想对它们进行排序,您可以使用async/await 相当轻松地对这两个操作进行排序,然后当您执行return 时,它实际上会从***函数返回并停止进一步控制流。

如果您想坚持使用数据库的回调接口,那么您可能必须将第二个操作嵌套到第一个选项中,这样您就不会启动第二个操作,如果您要执行res.send( message: "No posts" ) .

【讨论】:

@HimanshuRanjan - 看看我在答案中添加了哪些关于修复它的选项。

以上是关于为啥我在 Nodejs 中收到“无法在将标头发送到客户端后设置标头”错误?的主要内容,如果未能解决你的问题,请参考以下文章

错误[ERR_HTTP_HEADERS_SENT]:无法在将标头发送到客户端后设置标头 - Express + Request

错误 1452 MySQL 和 NodeJS。为啥数据库不能正确引用我的表?

为啥我收到错误,因为不允许使用 nodejs 将文件从一个文件夹复制到另一个文件夹?

为啥我在使用 zip 或直接使用编辑器在 gcloud 中部署 nodejs 函数时遇到错误?

为啥我在nodejs中的代码会出错并要求我写catch,但我已经在代码中说了

为啥当我使用 nativescript 从 iOS 发送表情符号字符时会收到陌生字符串