繁体   English   中英

脚本和setTimeout / setInterval如何在NodeJS中一起工作?

[英]How can a script and setTimeout/setInterval work together in NodeJS?

通读NodeJS事件循环说明,我想知道setTimeoutsetInterval如何真正起作用。

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

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

让我们以身作则

setTimeout(function(){
    print('there');
});

print('hi');

这将打印hi然后there

这是发生了什么

该脚本将一直处理到最后一行,并且一旦找到计时器函数,它将把它添加到队列中,稍后由队列调度程序at the end of the execution时进行处理

loop queue => [ setTimeout ]

在退出之前,应该有一个调度程序,进行某种循环,以检查队列中是否有东西并处理它们,然后一旦所有计时器中都没有队列,循环就会退出。

假设我们在setInterval调用setTimeout

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 ]

回到主队列再次...

现在,这只是计时器的工作方式,它们并不是要处理阻塞代码,而是一种在一定间隔下运行代码的方法

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操作,打开/读取/写入文件

[编辑]哼哼重新阅读您的第一篇文章,我想我知道您有什么毛病。 您在帖子中提到了nodejs ,这意味着您可能正在对服务器进行编码。

如果您对服务器端JavaScript不太熟悉,而对php服务器更熟悉,例如,它可能确实令人困惑。

使用php服务器时,请求会创建一个新的线程来处理该线程,并且当主脚本 (如您所称)结束时,该线程将被杀死,并且该服务器上没有其他任何内容(监听请求的网络服务器除外,例如nginxapache )。

使用nodejs,就不同了。 主线程是单独的,并且始终在运行 因此,当请求到达时,将触发回调,但它们仍在该单个线程中。 否则说: 主脚本永远不会结束 (除非您杀死它或服务器崩溃:))


好吧,这是准确的。 由于JavaScript 具有单线程特性,如果计时器在主线程忙时结束,则计时器的回调将等待

当你做

setTimeout(callback, 1000)

您不是在说“我希望在1秒内调用此回调”,而实际上是“我希望在1s内调用此回调”

John Resig撰写的这篇文章非常出色,并详细介绍了JavaScript计时器的详细信息, 网址为https://johnresig.com/blog/how-javascript-timers-work/

但是,如果我在该脚本中调用setTimeout并期望脚本仍在运行时触发该怎么办?

你没想到。 您希望同步代码在超时发生之前运行到完成方式。

如果脚本仍在运行,因为它在进行阻止( 挂起) ,则超时回调没有机会执行,它将等待。 这就是为什么我们需要编写非阻塞代码。

那不是正常情况吗?

否。大多数情况下,没有JS执行时,事件循环处于空闲状态(尽管可能有后台任务在执行繁重的工作)。

鉴于Node是单线程的,因此它(v8引擎)始终在执行下一个脚本之前执行当前脚本。 因此,当我们使用主脚本启动节点服务器时,它先加载,解析,编译和执行该脚本,然后再运行其他脚本。 仅当当前正在运行的脚本命中I / O调用时,它才会被撞到事件循环的后面,从而为其他脚本或setTimeout回调提供执行的机会。 这是JavaScript引擎的本质,也是Node不适合长期运行于内存中的CPU密集型任务的原因。

就像@atomrc在他的回答中所说的那样, setTimeoutsetInterval只是节点在超时时间段运行回调的提示,没有保证。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM