Do NodeJS setInterval()s queue up? - javascript

There's a classical problem with setInterval() in browsers - if some JS code or other browser process takes too long to complete, several callback invocations might get "backed up" and you suddenly end with your callback executed multiple times in quick succession.
Often this is not what is desired when setInterval() is used. It's a typical use case when you want AT LEAST some interval of time to pass between invocations. The workaround to this is to use setTimeout() instead and only schedule the next invocation when the previous is completed.
This works well but also is extra code and might be confusing for someone who does not understand the issue.
I know that NodeJS works differently than browsers, but I cannot find any information on this particular aspect. Does NodeJS also exhibit the same behavior, or does it guarantee a minimum time between invocations when using setInterval()?

Looks like it.
start = () => {
console.log(Date.now(), 'interval started');
setInterval(() => console.log(Date.now(), '-'), 1000);
}
busy = (n) => {
console.log(Date.now(), 'busy started');
for (let i = 1; i < n; i++) Math.sqrt(i, 7);
console.log(Date.now(), 'busy done');
}
start();
busy(1e10); // this takes a while; nothing is printed, because this keeps the node.js thread busy
Output:
1642469880773 interval started
1642469880776 busy started
1642469888272 busy done
1642469888272 -
1642469889273 -
1642469890274 -
Note the long gap between busy start and done, and how no backlog of interval callbacks seem to follow.

Related

setTimeout triggers too late in MIDI.js

I'm using MIDI.js to play a MIDI file with several musical instruments.
The following things execute too late, how can I fix that?
First notes of the song. Like all notes, they are scheduled via start() of an AudioBufferSourceNode here.
MIDI program change events. They are scheduled via setTimeout here. Their "lateness" is even worse than that of the first notes.
When I stop the song and start it again, there are no problems anymore, but the delay values are very similar. So the delay values are probably not the cause of the problem.
(I use the latest official branch (named "abcjs") because the "master" branch is older and has more problems with such MIDI files.)
That is how JavaScript Event Loop works.
Calling setTimeout ... doesn't execute the callback function after the given interval.
The execution depends on the number of waiting tasks in the queue.
... the delay is the minimum time required for the runtime to process the request (not a guaranteed time).
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#zero_delays
Instead of setTimeout() you can use window.requestAnimationFrame() and calculate elapsed time for delay by yourself.
Window.requestAnimationFrame() - Web APIs | MDN
The window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update an animation before the next repaint. The method takes a callback as an argument to be invoked before the repaint.
... will request that your animation function be called before the browser performs the next repaint. The number of callbacks is usually 60 times per second, but will generally match the display refresh rate in most web browsers
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
performance.now() - Web APIs | MDN
https://developer.mozilla.org/en-US/docs/Web/API/Performance/now
In our situation, we don't want to do any animation but want to just use it for a better-precision timeout.
const delayMs = 1000;
const startTime = performance.now();
function delay(func) {
const delayStartTime = performance.now();
function delayStep() {
// Run again if still elapsed time is less than a delay
if (performance.now() - delayStartTime <= delayMs) {
window.requestAnimationFrame(delayStep);
}
else
{
// Run the delayed function
func();
}
}
// Run first time
window.requestAnimationFrame(delayStep);
}
// Trying `setTimeout()`
setTimeout(() => doSomeJob('setTimeout()'), delayMs);
// Trying `delay()`
delay(() => doSomeJob('delay()'));
// Function that we'd like to run with a delay
function doSomeJob(marker)
{
const elapsedTime = performance.now() - startTime;
console.log(`${marker}: Ran after ${elapsedTime / 1000} seconds`);
}
If you run it many times, you'll see that delay() is pretty much all the time better than setTimeout(). The difference is very small because there is nothing else happens on the page. If there will be something intensive running, setTimeout() should demonstrate worse "precision".

Accurate javascript sleep function

I am using javascript to send a bench of request at regular interval (every 5 ms).
I tried to use setTimeout and also sleep function, but none of them have accurate timing.
They ensure that the time interval is >= 5ms but not == 5ms.
Any idea?
It seems that this very difficult to achieve in javascript or even impossible!!
This is the code I am using:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sendRequest(){
var i;
for (i=1; i<= numberOfRequests; i++){
// send my i^th request here
await sleep(5);
}
}
There isn't any way to provide exact timeouts in any programming language as much as they live in a general purpose multiprogrammed operating system. That happens because the exact moment the operating system will give its time slice to a particular process is just unpredictable.
Furthermore, JavaScript is single-threaded and it works with an event loop system, and the asyncrhonous tasks (such as setTimehout, xhr callback, click listeners and so on) will be executed only after that all the current code is finished. For example, if you have:
setTimeout(() => console.log('hello world'), 500);
for (let i = 0; i<1E100; i++) {
console.log(Math.sqrt(i));
}
Hello world will be only printed only after all the calculations are completed.
Since javascript uses a single threaded event loop there is no way to obtain that accuracy. The events in the event loop are executed when the engine has finished the previous task. If you set a task to be executed after 20 ms (setTimeout, setInterval or any other custom task) the engine will add that task to the event loop then it will try to run any other task from the loop (let's assume a function that takes 25 ms to run). Since javascript is single thread, you can start any other element from the loop, until the 25ms task is finished. In that case, your timeout will start after 25 ms, even if you set it to 20. This is how javascript architecture works.
Even if you implement multi thread (workers, threads etc.) the event loop will still be present in each of them (each thread has its own loop)

Node.js: breakpoints in setInterval

I am debugging a node.js (Node version 6.2.1) program with setInterval() in it. The code is very simple:
const log = console.log;
let cnt = 0;
const inc = () => {
const beforeDebug = Date.now();
log('Before debug time', beforeDebug);
debugger;
const afterDebug = Date.now();
log('After debug time', Date.now());
log('Time spent in debug', afterDebug - beforeDebug);
log(`[${cnt++}]`);
};
setInterval(inc, 1000);
```
The strange behaviour that I observe is that the pauses between setInterval() callback execution will depend on how much time I spend in a breakpoint. For example, if I stay in 'debugger' line for 10 seconds and then resume, I will see the next number only in 10 seconds after I resumed.
I checked this behaviour with command line node debugger and node-inspector.
[UPDATE] (added timestamps to code)
Here's an extracted lines of debug session in command line:
Debugger listening on port 5858
connecting to 127.0.0.1:5858 ... ok
< Before debug time 1467952218915
< After debug time 1467952235018
< Time spent in debug 16103
< Before debug time 1467952252123
Basically the time difference between the executions of the callback in the last section is 17105 which is almost exactly <time-in-debug> + <interval-value>
This issue doesn't have a significant practical impact, but I would love to understand the mechanism of timer underneath. Why it behaves in such weird way?
The short answer is that the time used to determine when to trigger callbacks is cached and can get out of sync with a callback's actual insertion time.
How is this possible?
To understand why this happens, it helps to have an idea of how node's timers are implemented (which is very well documented in the source). Here, all we need to remember is that:
Callbacks are stored in a map keyed by timeout. For example, when you run setTimeout(foo, 10), then foo will be added to a list of all callbacks with timeout 10.
Callbacks keep track of the time at which they were inserted. We'll refer to it as insertionTime to keep things simple (it's actually timer._idleStart in the source).
When a timeout kicks in, node records the current time (now) and runs the timeout's callbacks in sequence until either no callbacks are left or a callback's insertion time is such that now - insertionTime < timeout. In that last case, node will delay the next wakeup of this timeout until timeout - (now - insertionTime).
In your case, here is what happens:
t=0, inc is inserted in a callback list with insertionTime=0.
t=1000, the timeout wakes up, records now=1000, and runs inc.
t=3000 (or however long you are debugging for), inc completes and gets reinserted in the callback list (since you are using setInterval), this time with insertionTime=3000.
t=3000, node keeps going through the list of callbacks and finds the newly inserted callback. It computes now - insertionTime = -2000 which is smaller than timeout (because now is still equal to 1000!). It therefore schedules the next callback execution 3000 = 1000 - (-2000) milliseconds later.
t=6000, the timeout wakes up again, records now=6000, and runs inc.
...
You can get some visibility on timer internals by running your program with NODE_DEBUG=timer:
$ NODE_DEBUG=timer node setinterval.js
TIMER 98831: no 1000 list was found in insert, creating a new one
TIMER 98831: timeout callback 1000
TIMER 98831: now: 1067
inc
TIMER 98831: 1000 list wait because diff is -2000
TIMER 98831: timeout callback 1000
TIMER 98831: now: 6072
inc
TIMER 98831: 1000 list wait because diff is -2000
...
As far as I can tell, this looks like a bug in node. I don't see when it would make sense to have a negative diff above.
What can we do about it?
You seem to be asking this more out of curiosity, but in case you ever need to prevent this added delay, you can work around it by making sure that you have at least one other callback for the same timeout. This will force an update to now.
function f() {
var end = Date.now() + 2000;
while (Date.now() < end); // 2 second busy loop.
console.log('f: ' + Date.now());
}
setInterval(f, 1000);
setTimeout(function () {
setInterval(function () {}, 1000);
}, 10); // Small delay to make sure they don't run in the same wakeup.
If we only add f, it would end up running every 5 seconds. However, if we also register the (empty) second callback, f will correctly run every 3 seconds!
Node.js runs your code in one single thread.
When you pause the execution, the consequential callback will also be delayed until you resume.
Here is an alternative example without employing debugger
var blockUntil = Date.now() + 7000;
function f() {
console.log('Before: ', Date.now())
while (Date.now() < blockUntil); // busy loop
console.log(' After: ', Date.now())
}
setInterval(f, 1000);
And I can confirm it can be reproduced without debugger.
Before: 1467966150871
After: 1467966156847
Before: 1467966163851
After: 1467966163851
Before: 1467966164856
After: 1467966164856
Before: 1467966165859
After: 1467966165859
Before: 1467966166864
After: 1467966166864

Is node.js setTimeout() working?

I'm new to Node.js. Is there something I need to do to get setTimeout() to work?
Here's a code snippet.
async code that sets appMsg.doneLoadTables = true when done
do {
console.log('waiting ... ' + appMsg.doneLoadTables);
setTimeout(function() { console.log('waiting ...'); }, 1000);
} while (!appMsg.doneLoadTables);
Symptoms:
(While the two calls to console.log are similar, only the first prints the value of appMsg.doneLoadTables.) Every result includes that value.
The spacing between calls to console.log is much closer than 1000 msec. (I suspect the spacing is as fast as the computer can process the loop shown here.)
While I would hope the async routines could continue to process during the delays I intended here, I've never seen this loop finish; it's as if the loop takes all processing resources and prevents the async routines from finishing their work and from setting the variable that'll end this loop.
I had this experience with Node 4.2.1; I continue to have this experience after installing Node 5.0.0.
I've seen that similar questions about setTimeout() have been asked here many times before. I hope my use of a IIFE inside setTimeout() makes this question distinct from all of those.
Thanks in advance for any help offered ...
JavaScript is single-threaded. setTimeout is not a form of sleep which pauses code at that line. It works by "scheduling" your callback for later, and execute it when the stack exhausts (the engine doing nothing) and is at least n milliseconds later, where n is the delay you placed in milliseconds.
Now your code doesn't work because it never exits the loop. The code doesn't get the chance to execute other code (the code you hope to run and change appMsg.doneLoadTables's value). All it does keep logging "waiting... [something]".
Essentially you are polling. What you could use instead is setInterval. When appMsg.doneLoadTables is true, you stop the polling by using clearInterval.
I am not 100% sure what is your goal ... however maybe this snippet takes you where you want to go (I opted for setTimeout instead of setInterval):
var appMsg = {doneLoadTables: false};
var stillLoading = function() {
if(false === appMsg.doneLoadTables) {
console.log('waiting ... ' + appMsg.doneLoadTables);
setTimeout(stillLoading, 50);
}
else {
console.log('Loading complete.');
process.exit();
}
}
stillLoading();
setTimeout(function(){
console.log('Setting appMsg.doneLoadTables = true');
appMsg.doneLoadTables = true;
}, 1000);
The script polls status every 50ms and marks "done" exactly after 1 second.
The output looks like this
waiting ... false
waiting ... false
waiting ... false
waiting ... false
...
Setting appMsg.doneLoadTables = true
Loading complete.
(While the two calls to console.log are similar, only the first prints the value of appMsg.doneLoadTables.) Every result includes that value.
That is the correct behavior since you never exit the while loop. You stay in the same event frame that keeps looping forever.
The spacing between calls to console.log is much closer than 1000 msec. (I suspect the spacing is as fast as the computer can process the loop shown here.)
That is the correct behavior again because you callbacks that you passed to setTimeout will never execute unless you exit the do-while loop, which you never do. So you just keep calling first console.log statement then you add a callback to event loop to execute in 1000 ms without ever giving it (the callback that you pass) the chance to execute.
While I would hope the async routines could continue to process during the delays I intended here, I've never seen this loop finish; it's as if the loop takes all processing resources and prevents the async routines from finishing their work and from setting the variable that'll end this loop.
The loop never finish because it doesn't have logic implemented that finishes it. "Async routines" can't continue because that would require exiting the current event frame (that runs infinite loop) and starting the next one that has you callback that you passed to setTimeout.
Hope my explanations will help you to understand how asynchronous JavaScript works.

Timing in javascript sequencer

I have developed a music sequencer in javascript; something like this: http://stepseq.michd.me/
I have implemented loop using setInterval function in following way:
var Sequencer = {
...
// every step sequencer ...
next: function(callback) {
// restart from begin if arrive to last sequecer step
if(Sequencer.current==Sequencer.steps.length)
Sequencer.current = 0;
// play all sounds in array step contains
if(Sequencer.steps[Sequencer.current].length>0) {
var set = Sequencer.steps[Sequencer.current];
for(var i=0;i<set.length;i++){
set[i].play();
}
}
callback(Sequencer.current);
Sequencer.current++;
},
loop: function(callback) {
Sequencer.interval = $interval(function(){
Sequencer.next(callback);
}, Sequencer.time);
}
}
...
Code below works but i think that there is a better way to implement loop; infact sometimes steps goes out of time. Sequencer.time (time passed to setInterval function) is a time in millisecs (this value is the conversion of a bpm value; for example it can be 125),
Someone can suggest me a better solution?
N.B.: this is a web application angularjs based (for this reason in code above a use $interval insteed of setInterval), but i think that this point is insignificant.
Javascript timer intervals are not guaranteed to run at exactly the time you request, due to the single threaded nature of JS. What you get is a callback that is queued up to run after the interval expires, whenever the engine is free to run it.
John resig has covered this off in some depth:
http://ejohn.org/blog/how-javascript-timers-work/
http://ejohn.org/blog/analyzing-timer-performance/
And from his conclusions, which is going to be important for your app:
If a timer is blocked from immediately executing it will be delayed
until the next possible point of execution (which will be longer than
the desired delay).
I don't really have a better solution for your problem, due to these fundamental issues with timers in JS, but this may at least explain what is happening.

Categories