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".
Related
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.
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)
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
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.
I'm using [youtube api ][1] to get to know when a video is fully buffered player.getVideoLoadedFraction()
when the fraction is 1, the video is fully buffered, but I have to poll this function to check whether it is 1 and then get the time, like:
setInterval(display_fraction,1);
since a video could be tens of minutes.
Will this polling creates a heavy load on the browser/client and thus affect the video streaming? are there any other better polling methods or ways to detect when youtube finishes buffering?
BTW, the link for youtube api is:
https://developers.google.com/youtube/flash_api_reference#Playback_controls
Humans start perceiving time intervals somewhere between a 20th and a 10th of a second, so trying to poll with a value of 1ms is neither necessary nor desireable (any modern browser will round that up to 5ms or 10ms anyway). Values like 50 or 100 would be more appropriate.
I would also strongly recommend using a chained series of setTimeout calls rather than a setInterval call, something like this:
function onVideoReady(callback) {
// Do first check as soon as the JavaScript engine is available to do it
setTimeout(checkVideoReady, 0);
// Our check function
function checkVideoReady() {
if (videoIsReady) {
// The video is ready, notify calling code
callback();
}
else {
// Not ready yet, wait a 10th of a second
setTimeout(checkVideoReady, 100);
}
}
}
...which you then use like this:
onVideoReady(function() {
// The video is ready, do something
});
The reasons I advocate a chained series of setTimeout instead of setInterval are:
You can change the delay easily from iteration to iteration. For instance in the above, I fire the check as soon as possible the first time, then then after 100ms each subsequent time. You can do more complex things with timing than that, the flexibility is there.
It's much, much harder to inadvertently end up with more than one running, since code has to explicitly trigger the next loop.
setInterval varies amongst browsers about whether it measure the interface from the start of the last call or the end of it. If you use a pattern like the above, you're always sure it's from the end of the last check.
If your code is still running when the next interval is meant to occur, it just gets skipped. This can cause gaps (e.g., if you're doing something every 100ms and your previous loop takes 102ms to complete, the next one doesn't start as soon as possible, it waits the remaining 98ms), at least on some browsers.
But it's up to you, of course, the above can be done just as easily with setInterval and clearInterval calls as with a chain of setTimeout calls.
An alternative to chained timeouts is chained Promises. The following implements periodic polling along with a timeout.
var Promise = require('bluebird');
/**
* Periodically poll a signal function until either it returns true or a timeout is reached.
*
* #param signal function that returns true when the polled operation is complete
* #param interval time interval between polls in milliseconds
* #param timeout period of time before giving up on polling
* #returns true if the signal function returned true, false if the operation timed out
*/
function poll(signal, interval, timeout) {
function pollRecursive() {
return signal() ? Promise.resolve(true) : Promise.delay(interval).then(pollRecursive);
}
return pollRecursive()
.cancellable()
.timeout(timeout)
.catch(Promise.TimeoutError, Promise.CancellationError,function () {
return false;
});
}
You call it like so.
poll(isVideoReady, pollingInterval, timeout).then(console.log);
See Javascript polling with promises.