简体   繁体   English

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

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

Reading through the NodeJS Event Loop description I wonder how setTimeout and setInterval can actually work. 通读NodeJS事件循环说明,我想知道setTimeoutsetInterval如何真正起作用。

The page says NodeJS first runs the given script (let REPL alone for now) and then enters the event loop. 页面上说NodeJS首先运行给定的脚本(现在暂时不使用REPL), 然后进入事件循环。 But what if I call setTimeout in that script and expect it to trigger while the script is still running? 但是,如果我在该脚本中调用setTimeout并期望脚本仍在运行时触发该怎么办? Isn't that the normal case actually? 那不是正常情况吗? According to the description the timer callback will not be triggered before the main script ends, which sounds really weird to me. 根据描述,在主脚本结束之前不会触发计时器回调,这对我来说真的很奇怪。

For those interested, here's the NodeJS outer even loop (there are actually 2 nested loops): https://github.com/nodejs/node/blob/master/src/node.cc#L4526 对于那些感兴趣的人,这里是NodeJS外部偶数循环(实际上有2个嵌套循环): https : //github.com/nodejs/node/blob/master/src/node.cc#L4526

let's do this by example 让我们以身作则

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

print('hi');

this will print hi then there 这将打印hi然后there

here's what happen 这是发生了什么

the script will be proccessed until last line and when ever it finds a timer function it will add it to a queue which will be handled later at the end of the execution by the queue scheduler 该脚本将一直处理到最后一行,并且一旦找到计时器函数,它将把它添加到队列中,稍后由队列调度程序at the end of the execution时进行处理

loop queue => [ setTimeout ]

before exit there should be a scheduler, some kind of a loop to check if we have something in the queue and handle them, then once queue is out of all timers the loop will exit. 在退出之前,应该有一个调度程序,进行某种循环,以检查队列中是否有东西并处理它们,然后一旦所有计时器中都没有队列,循环就会退出。

let's suppose we call setTimeout inside setInterval 假设我们在setInterval调用setTimeout

setInterval(function(){
    setTimeout(function(){
        print('hi')
    }, 500);
}, 1000);

loop queue => [ setInterval ]

after 1000 ms 1000 ms

setInterval will be fired and the inner setTimeout will be added to the queue 将触发setInterval并将内部setTimeout添加到队列中

loop queue => [ setTimeout, setInterval ]

now we get back to the main loop which will wait for another 500 ms an fire the inner setTimeout function, then remove it from the queue because setTimeout should be run once. 现在我们回到main loop ,它将再等待500毫秒触发内部setTimeout函数,然后将其从队列中删除,因为setTimeout应该运行一次。

loop queue => [ setInterval ]

back to the main loop, we still have items in the queue, so it will wait another 500 ms and fire again ( 500 + 500 = 1000 ms) the inner setTimeout function will be added to the queue again 回到主循环,队列中仍然有项目,因此它将等待另外500 ms并再次触发( 500 + 500 = 1000 ms)内部setTimeout函数将再次添加到队列中

loop queue => [ setTimeout, setInterval ]

back to the main queue agin and again ... 回到主队列再次...

Now this is simply how timers work, they are not meant to handle blocking code, it's a way to run code at some intervals 现在,这只是计时器的工作方式,它们并不是要处理阻塞代码,而是一种在一定间隔下运行代码的方法

setInterval(function(){
    // do something long running here
    while (1) {}
    setTimeout(function(){
        print('hi')
    }, 500);
}, 1000);

main loop will block here and the inner timeout will not be added to the queue, so this is a bad idea 主循环将在此处阻塞,并且内部超时不会添加到队列中,所以这是一个坏主意

nodejs and event loop in general are good with network operations because they don't block when used with select for example. 通常,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 which is the event loop used in nodejs, uses threads to handle some blocking operations like IO operations, open/read/write files libuv是nodejs中使用的事件循环,它使用线程来处理一些阻塞操作,例如IO操作,打开/读取/写入文件

[EDIT] humm re-reading your initial post, I think I know what bugs you. [编辑]哼哼重新阅读您的第一篇文章,我想我知道您有什么毛病。 You mentioned nodejs in your post, implying you might be coding a server. 您在帖子中提到了nodejs ,这意味着您可能正在对服务器进行编码。

If you are not really familiar with server side JavaScript and more used to php server for example it might be very confusing indeed. 如果您对服务器端JavaScript不太熟悉,而对php服务器更熟悉,例如,它可能确实令人困惑。

With a php server, a request creates a new thread that will handle it and when the main script (as you call it) ends, then the thread is killed and nothing else runs on the server (except for the webserver that listens to request, like nginx or apache ). 使用php服务器时,请求会创建一个新的线程来处理该线程,并且当主脚本 (如您所称)结束时,该线程将被杀死,并且该服务器上没有其他任何内容(监听请求的网络服务器除外,例如nginxapache )。

With nodejs, it's different. 使用nodejs,就不同了。 The main thread is alone and always running . 主线程是单独的,并且始终在运行 So when a request arrives, callbacks are fired but they are still in that single thread. 因此,当请求到达时,将触发回调,但它们仍在该单个线程中。 Said otherwise: the main script never ends (except when you kill it or that your server crashes :) ) 否则说: 主脚本永远不会结束 (除非您杀死它或服务器崩溃:))


Well, that is accurate. 好吧,这是准确的。 Because of the single-threaded nature of JavaScript, if a timer ends while the main thread is busy, the timer's callback will wait . 由于JavaScript 具有单线程特性,如果计时器在主线程忙时结束,则计时器的回调将等待

When you do 当你做

setTimeout(callback, 1000)

You are not saying "I want this callback to be called in exactly 1s" but actually "I want this callback to be called in, at least, 1s" 您不是在说“我希望在1秒内调用此回调”,而实际上是“我希望在1s内调用此回调”

This article by John Resig is an excellent read and goes through the details of the JavaScript's timers https://johnresig.com/blog/how-javascript-timers-work/ John Resig撰写的这篇文章非常出色,并详细介绍了JavaScript计时器的详细信息, 网址为https://johnresig.com/blog/how-javascript-timers-work/

But what if I call setTimeout in that script and expect it to trigger while the script is still running? 但是,如果我在该脚本中调用setTimeout并期望脚本仍在运行时触发该怎么办?

You don't expect that. 你没想到。 You expect your synchronous code run to completion way before the timeout occurs. 您希望同步代码在超时发生之前运行到完成方式。

If the script is still running, because it's doing something blocking - it hangs - then the timeout callback doesn't get a chance to execute, it will wait. 如果脚本仍在运行,因为它在进行阻止( 挂起) ,则超时回调没有机会执行,它将等待。 That's exactly why we need to write non-blocking code. 这就是为什么我们需要编写非阻塞代码。

Isn't that the normal case actually? 那不是正常情况吗?

No. Most of the time no JS is executing, the event loop is idling (while there might be background tasks doing the heavy lifting). 否。大多数情况下,没有JS执行时,事件循环处于空闲状态(尽管可能有后台任务在执行繁重的工作)。

Given that Node is single threaded, it (v8 engine) always executes the current script before moving on to the next one. 鉴于Node是单线程的,因此它(v8引擎)始终在执行下一个脚本之前执行当前脚本。 So when we start a node server with a main script, it loads, parses, compiles and executes that script first, before it runs anything else. 因此,当我们使用主脚本启动节点服务器时,它先加载,解析,编译和执行该脚本,然后再运行其他脚本。 Only if the current running script hits an I/O call it gets bumped out to the back of the event loop, giving other scripts or setTimeout callbacks a chance to execute. 仅当当前正在运行的脚本命中I / O调用时,它才会被撞到事件循环的后面,从而为其他脚本或setTimeout回调提供执行的机会。 This is the very nature of JavaScript engine and the reason Node is not considered good for long running, in-memory CPU intensive tasks. 这是JavaScript引擎的本质,也是Node不适合长期运行于内存中的CPU密集型任务的原因。

As @atomrc said in his answer, setTimeout and setInterval are just a hint to node to run the callbacks after the timeout period, there are no guarantees. 就像@atomrc在他的回答中所说的那样, setTimeoutsetInterval只是节点在超时时间段运行回调的提示,没有保证。

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

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