脚本和setTimeout / setInterval如何在NodeJS中协同工作?

时间:2017-06-15 15:55:59

标签: javascript node.js

阅读NodeJS Event Loop description我想知道setTimeoutsetInterval实际上是如何运作的。

该页面说NodeJS首先运行给定的脚本(现在单独使用REPL),然后进入事件循环。但是,如果我在该脚本中调用setTimeout并期望在脚本仍在运行时触发该怎么办?这不是正常情况吗?根据描述,在主脚本结束之前不会触发定时器回调,这听起来很奇怪。

对于那些感兴趣的人,这里是NodeJS外部偶数循环(实际上有2个嵌套循环):https://github.com/nodejs/node/blob/master/src/node.cc#L4526

4 个答案:

答案 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服务器,请求创建一个新线程来处理它,当主脚本(当你调用它)结束时,线程被终止,服务器上没有其他任何东西运行(听取请求的网络服务器除外,例如nodejsnginx)。

使用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在他的回答中所说,setTimeoutsetInterval只是提示节点在超时期后运行回调,没有任何保证。< / p>