简体   繁体   中英

Allow running setInterval more than once per millisecond in nodejs

I have a node script which is supposed to utilize all the CPU resources a single node process could get. But I found setInterval to be too slow.

And sure enough I found this in the documentation:

When delay is larger than 2147483647 or less than 1, the delay will be set to 1.

source: https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args

Now I wonder if there is a way to reduce the limit further or if there is an alternative function that I could use.

I can't just use a normal loop because there are other asynchronous things that need to run at the same time.

Edit:
Again: I can't just use a normal loop because there are other asynchronous things that need to run at the same time. I'm not sure why this is so hard to understand.

While a normal loop is running, you are blocking the execution of everything else. It doesn't matter if you put the loop in another asynchronously executed function.

What does this mean?

Let's look at some examples:

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

while (true) {
    // do whatever
}

What will this code do? It will block everything. console.log('a') will not be executed continuously.

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

This will also block the execution of the intervals as soon as the while loop starts.

I believe the question belongs to node rather than to browser. You can use some of the following options (recursively/in loop) for reducing your delay time.

setImmediate

setImmediate - Schedules the "immediate" execution of the callback after I/O events' callbacks. Returns an Immediate for use with clearImmediate ().

When multiple calls to setImmediate() are made, the callback functions are queued for execution in the order in which they are created. The entire callback queue is processed every event loop iteration. If an immediate timer is queued from inside an executing callback, that timer will not be triggered until the next event loop iteration.

It's from node guides:

setImmediate and setTimeout are similar, but behave in different ways depending on when they are called.

  • setImmediate() is designed to execute a script once the current poll phase completes.
  • setTimeout( ) schedules a script to be run after a minimum threshold in ms has elapsed.

process.nextTick

The process.nextTick() method adds the callback to the "next tick queue". Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called.

From node guide

We recommend developers use setImmediate() in all cases because it's easier to reason about (and it leads to code that's compatible with a wider variety of environments, like browser JS.)

1 setInterval multiple times run more!

 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) 

在此输入图像描述

2. setTimeout recurser run less!

 let count = 0, go = true recurser() setTimeout(() => { go = false console.log(`count:${count}`) }, 100) function recurser() { count++ go && setTimeout(recurser) } 
在此输入图像描述

3.requestAnimationFrame run less!

 let count = 0, go = true, timelimit = 100 step() setTimeout(() => { go = false, console.log(`count:${count}`) }, timelimit) function step() { count++ go && requestAnimationFrame(step) } 
在此输入图像描述

so as I know ,run setInterval multiple times, and I believe while will count more

Thanks to Josh Lin for the idea to just run multiple intervals. I ended up with two simple wrapper functions for setInterval and clearInterval :

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);
}

It works just like the original functions:

var count = 0;

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

// stop execution:
clearInterval2(foo)

You ask if it is possible to

run setInterval more than once per millisecond in nodejs

As you note in your question, this is not possible with setInterval , since there is always a minimum delay of at least 1 ms in node.js . In browsers, there is often a minimum delay of at least 10 ms .

However, what you want to achieve—to repeatedly run CPU-intensive code without unnecessary delay—is possible in other ways.

As noted in the answer by The Reason , setImmediate is a good option available in node.js. As setImmediate has limited browser support, and is unlikely to be widely supported in the future , there is another approach that also works in browsers.

While browsers enforce a minimum delay for setInterval and setTimeout , the delay for setTimeout is enforced from when the timer is set, not when it is run. If we use setTimeout repeatedly to call CPU-intensive code, we can make sure that a timer is always set 10–15 ms in advance (if the code takes at least 10–15 ms to run), thus reducing the actual delay to 0 ms.

The demo snippet below borrows code from this answer to demonstrate how the use of timers set in advance may make the delay smaller than what is enforced. In the browsers I tested, this usually results in a 0 ms delay.

 // 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); } 

I think you can solve your problem using async module... a way could be:

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

But take into account this note from the official documentation...

Note: parallel is about kicking-off I/O tasks in parallel, not about parallel execution of code. If your tasks do not use any timers or perform any I/O, they will actually be executed in series. Any synchronous setup sections for each task will happen one after the other. JavaScript remains single-threaded.

In short answer, you can't. There are some limitation of Javascript/Node as it is a single thread application. That's why you have async interrupts.

The long answer: From the computer architecture's perspective, modern CPU and Kernel scheduling is not deterministic. If you want such fine grain control, I would suggest you look at MCU and embedded solutions that does not have a kernel scheduler. Because, your OS have many other processes and Kernel processes that takes up CPU time, so kernel scheduler have to keep scheduling different processes to be run on the CPU and meet many different demands.

Even with the set 1ms, when you try to measure, it is probably not 1ms (exact time depends on OS, hardware and amount of processes running on your computer).

Now, if you want to use all CPU resources, not possible.

But if you want to utilize as much as resources as possible, you can explore current programming pattern. For example, you can schedule 1 million threads (you machine probably won't be able to handle it), or some crazy large amount of processes and let your scheduler to be constantly putting process on your CPU, so there is no idle time and max out CPU utilization.

Alternatively, you can run CPU Stress tests, and those are designed to simply max out your CPU and keep it burning to high temperature -- make sure you have the cooling solution in place.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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