关于node.js:为什么我可以在”res.send”之后执行代码?

Why can I execute code after "res.send"?

我想知道以下代码行为背后的机制是什么:

1
2
res.send(200, { data: 'test data' });
console.log('still here...');

我的理解是 res.send 不会返回函数,但会关闭连接/结束请求。这可以解释为什么我仍然可以在 res.send 命令之后执行代码(我查看了 express 源代码,它似乎不是异步函数)。

还有什么我可能会遗漏的东西吗?


当然 end 会结束 HTTP 响应,但它不会对您的代码做任何特别的事情。

即使您结束了回复,您也可以继续做其他事情。

然而,你不能用 res 做任何有用的事情。由于响应已结束,您无法向其写入更多数据。

1
2
res.send(...);
res.write('more stuff'); // throws an error since res is now closed

这种行为与其他传统框架(PHP、ASP 等)不同,后者为 HTTP 请求分配线程并在响应结束时终止线程。如果您调用类似 ASP 的 Response.End 之类的等效函数,线程将终止并且您的代码将停止运行。在节点中,没有线程可以停止。 reqres 不会再触发任何事件,但回调中的代码可以继续运行(只要它不尝试调用 res 上需要打开有效响应的方法) .


编辑:我不再做下面解释的事情,因为你不应该在不需要它的时候返回一个值。它使您的代码可读性降低并且看起来很hackish。相反,我建议将 return 语句与 res.send() 分开。 @slavafomin 在评论中很好地解释了这一点。

停止执行函数并同时发送响应的一种简单方法是执行

1
return res.send('500', 'Error message here');

这允许您使用简短的 if 语句来处理错误,例如:

1
2
3
if (err) {
    return res.send('500', 'Error message here');
}

res.send 函数的确切返回是一个对象,它似乎包含您结束连接后的整个连接状态(请求、状态、标头等),但这应该不重要,因为您不会用它做任何事情。


一个可能的解决方案是使用这个库:on-finished

1
2
3
4
5
6
7
8
9
10
11
12
var destroy = require('destroy')
var fs = require('fs')
var http = require('http')
var onFinished = require('on-finished')

http.createServer(function onRequest (req, res) {
  var stream = fs.createReadStream('package.json')
  stream.pipe(res)
  onFinished(res, () => {
    destroy(stream)
  })
})

也适用于 Express,例如:

1
2
3
4
5
6
7
8
9
10
const express = require('express');
const app = express();

app.get('/video', (req, res) => {
 const ffmpeg = spawn(ffmpegPath, argsArray);
 ffmpeg.stdout.pipe(res);
 onFinished(res, () => {
   ffmpeg.kill();
 });
});