代码之家  ›  专栏  ›  技术社区  ›  Robert C. Holland

如何准确地在“真实世界”中每n毫秒调用一个函数?

  •  1
  • Robert C. Holland  · 技术社区  · 7 年前

    如果我理解正确, setInterval(() => console.log('hello world'), 1000)

    在一个复杂的程序中,node.js是否有可能在现实世界中每n毫秒调用一次函数?

    4 回复  |  直到 7 年前
        1
  •  2
  •   jfriend00    7 年前

    如果我理解正确,setInterval(()=>console.log('hello world'),1000)会将函数放置到要运行的任务队列中。但如果前面还有其他任务,它不会以1000毫秒或每次运行。

    没错。如果node.js恰好在计时器准备运行时忙于执行其他操作,则它不会在所需的时间运行。在运行计时器回调之前,node.js将等待它完成它的其他任务。你可以把node.js想象成它有一个单一的思想(一次只能做一件事),计时器永远不会中断正在运行的现有任务。

    在一个复杂的程序中,node.js是否有可能在现实世界中每n毫秒调用一次函数?

    不,在node.js中不可能这样做。js以单线程方式运行Javascript,它是事件驱动的,而不是抢占式的。所有这些都意味着您不能依赖于在精确的真实时间运行的代码。

    在node.js的封面下发生的事情是,您为将来的特定时间设置了一个计时器。这个计时器goes被注册到node.js事件循环中,以便每次通过事件循环时,它都会检查是否有任何挂起的计时器。但是,它只有在计时器准备启动之前运行的其他代码完成运行时才能通过事件循环。以下是事件的顺序:

    • 为将来的某个时间设置计时器(例如时间X)
    • 再运行一些代码
    • 再运行一些代码(当此代码运行时,时间X经过-计时器运行的时间)
    • 事件循环检查是否有任何挂起的计时器。它找到一个计时器并在X+n时调用其回调。

    因此,在大约时间X调用计时器的唯一方法是node.js在正好时间X没有其他事情要做。如果您的程序正在做任何其他事情,您不能保证您的程序在正好时间X可以在您希望的时间运行计时器。js在任何方面都不是一个实时系统。单线程和非先发制人意味着计时器可能需要等待node.js完成一些其他事情,然后才能运行,因此无法保证计时器将完全按时运行。相反,当解释器下一次可以返回到事件循环(完成运行当时可能正在运行的任何其他操作)时,它将在时间X之前运行。这可能接近时间X,也可能是一个重要的时间X。

    如果您真的需要在特定的时间精确地运行某些东西,那么您可能需要一个先发制人的系统(而不是node.js),它比node.js实时得多。


    否则,您可能希望用一种更实时的系统语言编写它,该语言具有先发制人的计时器(甚至可能具有线程优先级)。

        2
  •  2
  •   Basile Starynkevitch    7 年前

    你的问题是 operating system 具体来说,假设计算机正在运行一些(通常的)操作系统(如Windows、Android、Linux、MacOSX等)。我推荐阅读 Operating Systems: Three Easy Pieces 去了解更多。

    实际上,你的电脑还有很多 processes 由其操作系统管理。他们中的一些人可能在逃跑。您的计算机可能处于这样一种情况,即 其他 确切地 thrashing .

    real-time operating system . 但后来, node.js 可能不会跑。

    如何准确地在现实世界中每隔n毫秒调用一个函数?

    因为你的 进程(实际上是单线程的,在系统线程级别,请参见 pthreads(7) jfriend00's answer )可能无法从您的操作系统获得足够的资源(因此,如果其他进程加载您的计算机太多, node.js节点 会是 starved 不能像你想的那样进步;也要意识到 priority inversions

    在Linux上,另请参见 shed(7) chrt(1) , renice(1)

        3
  •  1
  •   Sachin Shah    7 年前

    克朗 async .

    npm install cron
    
    var CronJob = require('cron').CronJob;
    new CronJob('* * * * * *', function() {
       console.log('You will see this message every second');
    
       callYourFunc();
    
    }, null, true, 'America/Los_Angeles');
    

    更多信息请阅读 link

        4
  •  1
  •   busyfingers    7 年前

    也许您可以按照CertainPerformance在注释中建议的方式,生成一个工作线程并在它等待工作时阻塞它。这可能不是最优雅的方法,但至少可以将阻塞逻辑放在一边,这样就不会影响应用程序的其余部分。

    如果您不熟悉群集模块,请查看文档中的示例: https://nodejs.org/docs/latest-v10.x/api/cluster.html

    推荐文章