允许在nodejs中每毫秒运行一次setInterval

时间:2018-01-22 11:03:54

标签: javascript node.js loops setinterval

我有一个节点脚本,它应该利用单个节点进程可以获得的所有CPU资源。但是我发现setInterval太慢了。

我确实在文档中找到了这个:

  

当延迟大于2147483647或小于1时,延迟将为   设为1.

来源:https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args

现在我想知道是否有办法进一步减少限制,或者是否有可以使用的替代功能。

我不能只使用普通循环,因为还有其他需要同时运行的异步事件。

修改
再说一遍:我不能只使用普通循环,因为还有其他需要同时运行的异步事件。 我不确定为什么这么难理解。

当正常循环正在运行时,您阻止执行其他所有操作。如果将循环放在另一个异步执行的函数中并不重要。

这是什么意思?

让我们看一些例子:

setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background

while (true) {
    // do whatever
}

这段代码会做什么?它会阻止一切。 console.log('a')不会被连续执行。

setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background
setTimeout(()=>{
    while (true) {
        // do whatever
    }
}, 1)

这也会在while循环开始时阻止执行间隔。

6 个答案:

答案 0 :(得分:8)

我认为这个问题属于 node 而不是浏览器。您可以使用以下某些选项(递归/循环)来减少延迟时间。

setImmediate

  

setImmediate - 在I / O事件的回调之后安排“立即”执行回调。返回立即值以供clearImmediate()。

使用      

当对setImmediate()进行多次调用时,回调函数按照创建它们的顺序排队等待执行。每次事件循环迭代都会处理整个回调队列。如果立即计时器从执行的回调内部排队,则在下一次事件循环迭代之前不会触发该计时器。

来自node指南:

  

setImmediatesetTimeout相似,但行为不同   取决于何时被召唤的方式。

     
      
  • setImmediate()用于在当前轮询阶段完成后执行脚本。
  •   
  • setTimeout()安排在经过最小阈值(ms)后运行的脚本。
  •   

process.nextTick

  

process.nextTick()方法将回调添加到“下一个刻度”   队列“。一旦事件循环的当前转弯转到   完成后,当前在下一个tick队列中的所有回调都将是   调用。

来自node指南

  

我们建议开发人员在所有情况下都使用setImmediate(),因为它是   更容易推理(它导致代码与a兼容)   更广泛的环境,如浏览器JS。)

答案 1 :(得分:3)

多次

1 setInterval运行更多!

let count = 0,
  by = 100,
  _intervals = [],
  timelimit = 100
for (let i = 0; i < by; i++) {
  _intervals[i] = setInterval(() => count++, 1)
}
setTimeout(() => {
  _intervals.forEach(x => clearInterval(x))
  console.log(`count:${count}`)
}, timelimit)

enter image description here

2. setTimeout递归少跑!

let count = 0,
  go = true
recurser()
setTimeout(() => {
  go = false
  console.log(`count:${count}`)
}, 100)

function recurser() {
  count++
  go && setTimeout(recurser)
}
enter image description here

3.requestAnimationFrame 少跑!

let count = 0,
  go = true,
  timelimit = 100
step()
setTimeout(() => {
  go = false,
    console.log(`count:${count}`)
}, timelimit)

function step() {
  count++
  go && requestAnimationFrame(step)
}
enter image description here

据我所知,多次运行setInterval,我相信while会更多

答案 2 :(得分:3)

感谢Josh Lin想要运行多个间隔。我最终得到了setIntervalclearInterval两个简单的包装函数:

function setInterval2(cb,delay) {
    if (delay >= 1)
        return [setInterval(cb,delay)];
    var intervalArr = [];
    var intervalCount = Math.round(1/delay);
    for (var i=0; i<intervalCount; i++)
        intervalArr.push(setInterval(cb,1));
    return intervalArr
}

function clearInterval2(intervalArr) {
    intervalArr.forEach(clearInterval);
}

它的工作方式与原始功能相同:

var count = 0;

// run interval every 0.01 milliseconds:
var foo = setInterval2(function(){
    count++;
},0.01);

// stop execution:
clearInterval2(foo)

答案 3 :(得分:1)

你问是否可以

  

在nodejs中每毫秒运行一次setInterval

正如您在问题中所述,setInterval无法做到这一点,因为始终有minimum delay of at least 1 ms in node.js。在浏览器中,通常会有minimum delay of at least 10 ms

但是,你想要实现的目的 - 在没有不必要的延迟的情况下重复运行CPU密集型代码 - 可以通过其他方式实现。

正如The Reason的回答所述,setImmediate是node.js中的一个很好的选择。由于setImmediate的浏览器支持有限,并且unlikely to be widely supported in the future,因此还有另一种方法可以在浏览器中使用。

虽然浏览器强制执行setIntervalsetTimeout的最小延迟,但setTimeout的延迟是在设置计时器时强制执行的,而不是在运行时执行的。如果我们反复使用setTimeout来调用CPU密集型代码,我们可以确保计时器总是提前10-15毫秒设置(如果代码运行至少需要10-15毫秒),从而减少了实际延迟到0毫秒。

下面的演示片段借用了来自this answer的代码,以演示如何使用预先设置的计时器可以使延迟小于强制执行的延迟。在我测试的浏览器中,这通常会导致0 ms的延迟。

&#13;
&#13;
// First: repeat runCPUForAtLeast50ms() 10 times
// with standard repeated setTimeouts and enforced delays
testTimeout(10, function(){
  // Then: repeat runCPUForAtLeast50ms() 10 times
  // using a repeated set of queued setTimeouts
  // circumventing the enforced delays
  testTimeout(10, false, true);
});

function testTimeout(repetitions, next, multiple) {
  var delays = [];
  var lastCheck;
  var extraTimers;

  function runner() {
    if(lastCheck){
      delays.push((+new Date) - lastCheck);
    }
    if(repetitions > 0) {
      //process chunk
      runCPUForAtLeast50ms();
      //set new timer
      setTimeout(runner);
    } else if(repetitions === 0) {
      //report result to console
      console.log((multiple? 
        'Repeated multiple timers delays: ' : 
        'Repeated single timer delays: ') + delays.join(', '));
      //invoke next() function if provided
      next && next();
    }
    repetitions--;
    lastCheck = +new Date;
  }

  setTimeout(runner);

  if(multiple){
   // make sure that there are always a fixed
   // number of timers queued by setting extra timers
   // at start
   extraTimers = 10;
   while(extraTimers--)
     setTimeout(runner);
  }
}

function runCPUForAtLeast50ms() {
  var d = (+new Date) + 50;
  while(+new Date < d);
}
&#13;
&#13;
&#13;

答案 4 :(得分:0)

我认为您可以使用async模块来解决您的问题......方法可能是:

async.parallel([
  (callback) => {
    // do normal stuff
  },
  (callback) => {
    // do your loop
  }
], (err, results) => {
  // ...
});

但请考虑官方文件中的这一说明......

  

注意:parallel是关于并行启动I / O任务,而不是关于   并行执行代码。如果您的任务不使用任何计时器或   执行任何I / O,它们实际上将被串行执行。任何   每个任务的同步设置部分将在一个之后发生   其他。 JavaScript仍然是单线程的。

答案 5 :(得分:0)

简而言之,你不能。由于它是单线程应用程序,因此Javascript / Node存在一些限制。这就是你有异步中断的原因。

答案很长: 从计算机体系结构的角度来看,现代CPU和内核调度不是确定性的。如果你想要这样精细的颗粒控制,我建议你看一下没有内核调度程序的MCU和嵌入式解决方案。因为,您的操作系统有许多其他进程和内核进程占用CPU时间,因此内核调度程序必须不断调度在CPU上运行的不同进程并满足许多不同的需求。

即使设置为1ms,当您尝试测量时,它可能不是1ms(具体时间取决于操作系统,硬件和计算机上运行的进程数量)。

现在,如果要使用所有CPU资源,则无法实现。

但是如果你想尽可能多地利用资源,你可以探索当前的编程模式。例如,您可以安排100万个线程(您的机器可能无法处理它),或者一些疯狂的大量进程,让您的调度程序不断地将进程放在您的CPU上,因此没有空闲时间和最大化CPU利用率。

或者,您可以运行CPU压力测试,这些测试旨在简化CPU并使其保持高温 - 确保您已准备好冷却解决方案。