为什么 NodeJS http 服务器不会在无限循环中超时?
Why does a NodeJS http server not time out in an infinite loop?
我将 NodeJS http 服务器超时设置为 10 秒:
const httpServer = require('http').createServer(app);
httpServer.timeout = 10 * 1000;
对服务器的传入请求按预期在 10 秒后超时 运行 20 秒超时:
await new Promise(resolve => setTimeout(resolve, 20000));
当后端请求需要 20 秒时,服务器也会按预期超时,例如至:
http://httpstat.us/200?sleep=20000
但是运行死循环时不超时:
while(true) {}
这是为什么?
当您 运行 进入无限循环时,Javascript 中的其他事件都不能 运行。事件循环被阻塞。所以,连控制超时的定时器也不行运行。这是因为 Javascript 运行 在单个线程中使用您的 JS,而 setTimeout()
之类的东西是事件驱动的,而不是先发制人的。因此,如果您处于无限循环中,则事件循环无法处理任何事件,并且 setTimeout()
永远不会触发,这通常会导致 http 超时。
欢迎使用 node.js 的事件驱动架构。
而且,以防万一你想知道,这个:
while(true) {}
也会停止 node.js 中的所有其他 activity,而不仅仅是计时器。唯一可以 运行 的是工作线程,但在事件循环被阻塞的情况下,它们甚至无法与主线程通信。
仅供参考,您可以使用以下内容自行演示:
let start = Date.now();
function time() {
let delta = (Date.now() - start) / 1000;
return delta.toFixed(3);
}
console.log(`${time()}: Starting 500ms timer`)
// set timer for 500ms
setTimeout(() => {
console.log(`${time()}: timer callback called`);
}, 500);
// spin for 3 seconds
while (Date.now() - start < 3000) {}
console.log(`${time()}: finished while loop`);
您可以非常清楚地看到计时器在 while 循环结束之前不会触发。虽然计时器应该在 500 毫秒后触发,但直到 3 秒 while 循环完成后它才会处理其回调。
您可以在 settimeout 函数中调用相同的函数,而不是在无限 while 循环中调用 运行
const loop = () => {
setTimeout(() => {
console.log("TCS")
loop()
}, 5000);
}
loop()
我在模板引擎处理 circ 时遇到了类似的问题。用户数据中的引用。
我想出的解决方案绝对是 hack。但只要你能让事情异步,就有可能解决这个问题。
通过稍微调整 jfriend00 的代码,它可以按预期工作:
let start = Date.now();
function time() {
let delta = (Date.now() - start) / 1000;
return delta.toFixed(3);
}
console.log(`${time()}: Starting 500ms timer`)
// set timer for 500ms
setTimeout(() => {
console.log(`${time()}: timer callback called`);
}, 500);
function breath() {
return new Promise(resolve => setImmediate(resolve));
}
async function main() {
// spin for 3 seconds
while (Date.now() - start < 3000) {
await breath();
}
}
console.log(`${time()}: finished while loop`);
您可以将其更改为仅在 1% 的时间内呼吸或其他不会对性能造成太大影响的东西。
我将 NodeJS http 服务器超时设置为 10 秒:
const httpServer = require('http').createServer(app);
httpServer.timeout = 10 * 1000;
对服务器的传入请求按预期在 10 秒后超时 运行 20 秒超时:
await new Promise(resolve => setTimeout(resolve, 20000));
当后端请求需要 20 秒时,服务器也会按预期超时,例如至:
http://httpstat.us/200?sleep=20000
但是运行死循环时不超时:
while(true) {}
这是为什么?
当您 运行 进入无限循环时,Javascript 中的其他事件都不能 运行。事件循环被阻塞。所以,连控制超时的定时器也不行运行。这是因为 Javascript 运行 在单个线程中使用您的 JS,而 setTimeout()
之类的东西是事件驱动的,而不是先发制人的。因此,如果您处于无限循环中,则事件循环无法处理任何事件,并且 setTimeout()
永远不会触发,这通常会导致 http 超时。
欢迎使用 node.js 的事件驱动架构。
而且,以防万一你想知道,这个:
while(true) {}
也会停止 node.js 中的所有其他 activity,而不仅仅是计时器。唯一可以 运行 的是工作线程,但在事件循环被阻塞的情况下,它们甚至无法与主线程通信。
仅供参考,您可以使用以下内容自行演示:
let start = Date.now();
function time() {
let delta = (Date.now() - start) / 1000;
return delta.toFixed(3);
}
console.log(`${time()}: Starting 500ms timer`)
// set timer for 500ms
setTimeout(() => {
console.log(`${time()}: timer callback called`);
}, 500);
// spin for 3 seconds
while (Date.now() - start < 3000) {}
console.log(`${time()}: finished while loop`);
您可以非常清楚地看到计时器在 while 循环结束之前不会触发。虽然计时器应该在 500 毫秒后触发,但直到 3 秒 while 循环完成后它才会处理其回调。
您可以在 settimeout 函数中调用相同的函数,而不是在无限 while 循环中调用 运行
const loop = () => {
setTimeout(() => {
console.log("TCS")
loop()
}, 5000);
}
loop()
我在模板引擎处理 circ 时遇到了类似的问题。用户数据中的引用。
我想出的解决方案绝对是 hack。但只要你能让事情异步,就有可能解决这个问题。
通过稍微调整 jfriend00 的代码,它可以按预期工作:
let start = Date.now();
function time() {
let delta = (Date.now() - start) / 1000;
return delta.toFixed(3);
}
console.log(`${time()}: Starting 500ms timer`)
// set timer for 500ms
setTimeout(() => {
console.log(`${time()}: timer callback called`);
}, 500);
function breath() {
return new Promise(resolve => setImmediate(resolve));
}
async function main() {
// spin for 3 seconds
while (Date.now() - start < 3000) {
await breath();
}
}
console.log(`${time()}: finished while loop`);
您可以将其更改为仅在 1% 的时间内呼吸或其他不会对性能造成太大影响的东西。