node.js - 如何在“真实世界”时间内每 n 毫秒准确地调用一个函数?
问题描述
如果我理解正确,setInterval(() => console.log('hello world'), 1000)
会将函数放置到某个任务队列中运行。但是,如果它前面还有其他任务,它就不会完全以 1000 毫秒或每次运行。
在一个复杂的程序中,是否也可以使用 node.js 在现实世界中每隔 n 毫秒准确地调用某个函数?
解决方案
如果我理解正确, 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 任务抢占,并且会在非常接近所需时间的时候运行。请记住,即使这种解决方法仍然不是真正的实时系统,但可能会达到某些目的。
否则,您可能希望使用具有抢占式计时器(甚至可能具有线程优先级)的更实时的系统语言来编写它。
推荐阅读
- python - 如何修复“CreateView 缺少查询集”
- python - 删除 RabbitMQ (AMQP) 服务器上队列中的消息
- java - 创建初始索引时休眠搜索失败
- nginx - Nginx 重定向,如果不是主机,如果不是请求 uri
- python - 如何将字符串中的每个其他字符大写
- android - 打开时共享元素转换滞后
- reactjs - useEffect 多次设置 Redux 状态
- javascript - 设置方法调用后上下文状态不更新
- javascript - 为什么我在 iframe 中推送的脚本在父级中执行?
- python - 什么是 c++ 版本的 tensorrt.volume?