首页 > 解决方案 > 如何在“真实世界”时间内每 n 毫秒准确地调用一个函数?

问题描述

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

在一个复杂的程序中,是否也可以使用 node.js 在现实世界中每隔 n 毫秒准确地调用某个函数?

标签: node.jssetinterval

解决方案


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

那是对的。如果在计时器准备好运行时 node.js 碰巧忙于做其他事情,它将不会在所需的时间准确运行。node.js 将等到它完成它的其他任务,然后再运行计时器回调。您可以将 node.js 想象成它有一个单轨思维(一次只能做一件事),并且计时器永远不会中断正在运行的现有任务。

在一个复杂的程序中,是否也可以使用 node.js 在现实世界中每隔 n 毫秒准确地调用某个函数?

不,在 node.js 中不可能做到这一点。node.js 将您的 Javascript 作为单线程运行,它是事件驱动的并且不是抢占式的。所有这些都意味着您不能依赖在精确的真实时间运行的代码。

在 node.js 的幕后发生的事情是您为将来的特定时间设置了一个计时器。该计时器在 node.js 事件循环中注册,因此每次通过事件循环时,它都会检查是否有任何未决计时器。但是,只有在计时器准备好触发之前正在运行的其他代码完成运行时,它才会通过事件循环。以下是事件的顺序:

  • 运行一些代码
  • 为将来的某个时间设置计时器(比如时间 X)
  • 运行更多代码
  • 暂时无事可做
  • 运行更多代码(当此代码运行时,时间 X 过去了 - 计时器运行的时间)
  • 前一个代码块运行完毕,控制权在时间 X + n 时返回到 node.js 事件循环(在计时器 X 应该触发之后的某个时间)。
  • 事件循环检查是否有任何挂起的计时器。它找到一个计时器并在时间 X + n 调用它的回调。

因此,在大约时间 X 调用您的计时器的唯一方法是 node.js 在确切时间 X 无事可做。如果您的程序正在做其他事情,您不能保证您的程序将在确切的时间 X 在您希望它运行的时候准确地运行计时器。node.js 无论如何都不是一个实时系统。单线程和非抢占式意味着计时器可能必须等待 node.js 完成其他一些事情才能运行,因此无法保证计时器将准确按时运行。相反,当解释器下一次有空返回事件循环时,它将在时间 X 之前运行(完成运行当时可能正在运行的任何其他内容)。这可能接近时间 X,也可能是时间 X 之后的重要时间。

如果您确实需要在特定时间精确运行某些东西,那么您可能需要一个比 node.js 更实时的抢先系统(不是 node.js)。


您可以通过启动另一个 node.js 进程(您可以使用 child_process 模块)在 node.js 中创建一个“解决方法”,并在该其他进程中启动一个程序,除了为您的计时器提供服务并执行与该计时器关联的代码。然后,至少您的计时器不会被其他可能正在运行的 Javascript 任务抢占,并且会在非常接近所需时间的时候运行。请记住,即使这种解决方法仍然不是真正的实时系统,但可能会达到某些目的。

否则,您可能希望使用具有抢占式计时器(甚至可能具有线程优先级)的更实时的系统语言来编写它。


推荐阅读