阅读NodeJS Event Loop description我想知道setTimeout
和setInterval
实际上是如何运作的。
该页面说NodeJS首先运行给定的脚本(现在单独使用REPL),然后进入事件循环。但是,如果我在该脚本中调用setTimeout
并期望在脚本仍在运行时触发该怎么办?这不是正常情况吗?根据描述,在主脚本结束之前不会触发定时器回调,这听起来很奇怪。
对于那些感兴趣的人,这里是NodeJS外部偶数循环(实际上有2个嵌套循环):https://github.com/nodejs/node/blob/master/src/node.cc#L4526
答案 0 :(得分:2)
让我们通过示例
执行此操作setTimeout(function(){
print('there');
});
print('hi');
这将打印hi
然后there
这是发生的事情
脚本将被执行到最后一行,并且当它找到计时器功能时 它会将它添加到队列中,队列调度程序
将在稍后处理at the end of the execution
loop queue => [ setTimeout ]
我们假设我们在setTimeout
setInterval
setInterval(function(){
setTimeout(function(){
print('hi')
}, 500);
}, 1000);
loop queue => [ setInterval ]
1000 ms
之后
将触发setInterval,并将内部setTimeout
添加到队列
loop queue => [ setTimeout, setInterval ]
现在我们回到main loop
,等待另一个500
毫秒
触发内部setTimeout函数,然后将其从队列中删除
因为setTimeout应该运行一次。
loop queue => [ setInterval ]
回到主循环,我们仍然有队列中的项目,所以它会等待
另一个500 ms
再次触发( 500 + 500 = 1000 ms)
内部setTimeout函数将再次添加到队列
loop queue => [ setTimeout, setInterval ]
回到主队列agin并再次......
现在这只是定时器如何工作,它们并不是要处理阻塞代码,而是 一种以某种间隔运行代码的方法
setInterval(function(){
// do something long running here
while (1) {}
setTimeout(function(){
print('hi')
}, 500);
}, 1000);
主循环将在此处阻止,内部超时将不会添加到队列中,因此 是一个坏主意
nodejs和事件循环通常适用于网络操作,因为它们不会阻塞
例如,与select
一起使用。
setInterval(function(){
// check if socket has something
if (socketHasData( socket )){
processSocketData( socket );
}
// do something else that does not block
// maybe schedule another timer here
print('hello');
}, 1000);
libuv
这是nodejs中使用的事件循环,它使用线程来处理一些事件
阻止IO操作,打开/读/写文件等操作
答案 1 :(得分:1)
[编辑] humm重新阅读你的帖子,我想我知道你的错误。您在帖子中提到session_set_cookie_params(time() + 60*60*24, '/');
,暗示您可能正在编写服务器代码。
如果您对服务器端JavaScript并不熟悉,并且更习惯使用php服务器,那么它确实可能非常混乱。
使用php服务器,请求创建一个新线程来处理它,当主脚本(当你调用它)结束时,线程被终止,服务器上没有其他任何东西运行(听取请求的网络服务器除外,例如nodejs
或nginx
)。
使用nodejs,它是不同的。主线程是单独的,始终运行。因此,当请求到达时,将触发回调,但它们仍然在该单个线程中。否则说:主脚本永远不会结束(除非你杀了它或你的服务器崩溃:))
嗯,这是准确的。由于JavaScript的单线程特性,如果计时器在主线程忙时结束,则计时器的回调将等待。
当你这样做时
apache
你并不是说“我希望这个回调能够以1s的形式被调用”,但实际上“我想要调用这个回调,至少是1s”
John Resig撰写的这篇文章非常精彩,详细介绍了JavaScript的计时器https://johnresig.com/blog/how-javascript-timers-work/
答案 2 :(得分:1)
但是,如果我在该脚本中调用
setTimeout
并期望在脚本仍在运行时触发该怎么办?
你没想到。您希望同步代码在超时发生之前以完成方式运行。
如果脚本仍在运行,因为它正在阻塞 - 它挂起 - 那么超时回调没有机会执行,它会等待。这正是我们需要编写非阻塞代码的原因。
实际上这不是正常情况吗?
没有。大部分时间没有JS正在执行,事件循环是空闲的(虽然可能有后台任务正在进行繁重的工作)。
答案 3 :(得分:0)
鉴于Node是单线程的,它(v8引擎)总是在继续执行下一个脚本之前执行当前脚本。因此,当我们使用主脚本启动节点服务器时,它首先加载,解析,编译和执行该脚本,然后再运行其他任何内容。只有当前运行的脚本遇到I / O调用时,它才会被发送到事件循环的后面,从而为其他脚本或setTimeout
回调提供执行机会。这就是JavaScript引擎的本质,也是Node不适合长时间运行的内存CPU密集型任务的原因。
正如@atomrc在他的回答中所说,setTimeout
和setInterval
只是提示节点在超时期后运行回调,没有任何保证。< / p>