setTimeout(func,delay) seems to fire very precisely at the specified time, as long as the page is not running some script while it is trying to fire the function. But is there a way to take lag into account?
For example if I set a 3sec timeout and javascript runs some heavy code which makes the page leggy for a while. As long as processing the "heavy code" is done by the 3sec it will timeout after ~3sec.
Is there a way to take the "heavy code" processing time into account and timeout after 3sec + the time the page was blocked?
Here is a jsfiddle: http://jsfiddle.net/me2loveit2/mCj2J/
var timeStart = new Date().getTime();
setTimeout(test, 3000); //<-- timeout should be 100
function test() {
var timeAfter100MS = new Date().getTime();
$('body').append('Timeout Fired at: <br>' + (timeAfter100MS - timeStart) + 'ms<br> (should be ~3000, but it did not take the blocked time into account.)');
}
function block() {
for (var i = 0; i < 100000000; i++) {};
}
block();
block();
block();
var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');
As #adeneo pointed out, there is no such possibility. You simply can't know how effectively processor is running your code at the other end, or the tasks it is currently making which might slow it down further. Every case is different. setTimeout tries to match the specified time but very often, it just can't be exact.
I think the solution is just to change your mindset. Try to avoid long blocking synchronous operations such as for (var i = 0; i < 10000000; i++) {}; When you drop or modify these you can have more accurate setTimeout firing. The reason being, that there will be smaller executable chunks in the event queue.
Generally speaking, there are different ways to do processing of blocking events. For instance, you could look into Web workers or yielding setTimeout calls. (See links at the end of this post).
Hence, I don't know your specific case, but if you are trying to make many setTimeout calls just as in game programming (loops) solution is to try to alter future setTimeout calls to contain smaller value so the full loop will try to catch up the simulation to match the specific frame rate.
This is usually done with combination of requestAnimationFrame.
Short example of a loop which attemps to run 30 fps in the browser:
You can also view it in js fiddle
/**
* This is example to run loop with 30fps in the browser
*
*/
var gl = {
now: new Date().getTime(),
dt: 0.0,
last: new Date().getTime(),
// physics with 0.033333 steps
step: 1 / 30
},
frames = 0,
started = new Date().getTime();
/**
* Game loop
*
*/
var gameLoop = function () {
gl.now = new Date().getTime();
gl.dt = gl.dt + Math.min(1, (gl.now - gl.last) / 1000);
while (gl.dt > gl.step) {
gl.dt = gl.dt - gl.step;
// Increase frames
frames++;
if(frames === 30) {
// How long it took to execute 30 frames in 1000 ms ?
document.body.innerHTML = "We executed 30 frames in " + (new Date().getTime() - started) + " ms.";
started = new Date().getTime();
frames = 0;
}
}
// last
gl.last = gl.now;
// next
requestAnimationFrame(gameLoop);
};
// Start the game loop
gameLoop();
Hopefully, this gave you some ideas. Thus, don't forget to use css transitions and similar when those can be applied.
For further reading, I recommend:
Yielding setTimeout calls
How to avoid blocking the browser while doing heavy work
Using Web Workers
Cheers.
Not sure I understand the question 100%, but if you do something like this, you're able to see if the other stuff (heavy processing) is not done by the time the timeout runs. This should take about 5 seconds, if you switch the 2000000000 to a 20000 (less proccessing), it should come back at 3 seconds.
var timeStart = new Date().getTime();
setTimeout(test, 3000); //<-- timeout should be 100
var currentTime = new Date().getTime();
function test() {
if (currentTime - timeStart < 3000){
var timeAfter100MS = new Date().getTime();
$('body').append('Took less than 3 seconds - ' + (timeAfter100MS - timeStart)+"ms");
}else{
$('body').append('Took more than 3 seconds');
}
}
function block() {
for (var i = 0; i < 10000000; i++) {};
currentTime = new Date().getTime();
}
block();
block();
block();
var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');
If the 'important' code causes really significant lag, and precise timing is important, you can keep the precision by using two timeouts. The first timeout measures the lag and sets the second timeout accordingly.
Here's an example, using your code as a basis:
var timeStart = new Date().getTime();
var msDelay = 3000;
setTimeout(testLag, msDelay - 500);
function testLag() {
var timeTestLag = new Date().getTime();
$('body').append('testLag() fired at: ' + (timeTestLag - timeStart) + 'ms<br/>');
setTimeout(test, timeStart + msDelay - timeTestLag);
}
function test() {
var timeAfter100MS = new Date().getTime();
$('body').append('Timeout Fired at: <br>' + (timeAfter100MS - timeStart) + 'ms<br> (should be ~3000, but it did not take the blocked time into account.)');
}
function block() {
for (var i = 0; i < 1000000000; i++) {};
}
block();
block();
block();
block();
block();
var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');
Note that the block is significantly more intensive than yours - I added a zero to your 100000000, and added a couple of extra block() calls. You might need to adjust the figures to get a sensible level of block for your own machine.
Based on Mauno's Answer I came up with a solution to temporarily "track the lag" using an interval. I am setting an interval with short intervals to capture the delay and set another timeout if necessary. Here is the working example: http://jsfiddle.net/me2loveit2/mCj2J/14/
It is approximate, but always walls within 100ms of the target which is good enough for me. It could be even more accurate if I increase the interval rate, but what I got is good enough for me.
I know using timeout & interval is not the best but sometimes the only way. I am just using them for a couple of seconds on page load and that's it.
Here is the code:
var timeStart = new Date().getTime();
var aditionalTimeout = 0;
var myTimeout;
setTimer(3000);
block();
block();
block();
var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');
function setTimer(milliseconds) {
//allow additional time to account for the huge lag the page has on load
recoverLagTime(milliseconds);
myTimeout = setTimeout(function () {
if (!aditionalTimeout) {
test();
} else {
if (aditionalTimeout >= milliseconds) {
test();
return;
}
setTimer(aditionalTimeout);
}
}, milliseconds);
}
function recoverLagTime(timeoutTime) {
aditionalTimeout = 0;
var interval = 50;
var counter = Math.ceil(timeoutTime / interval);
var startTime = new Date().getTime();
var intervalTime;
var lagInterval = setInterval(adjustTimer, interval);
function adjustTimer() {
if (counter <= 0 || aditionalTimeout < 0) {
clearInterval(lagInterval);
return;
}
counter--;
intervalTime = new Date().getTime();
var diff = (intervalTime - startTime);
if (diff > (interval + 5)) {
aditionalTimeout += (diff - interval);
}
startTime = new Date().getTime();
}
}
function test() {
aditionalTimeout = -100;//stop the check function
var timeAfter100MS = new Date().getTime();
$('body').append('Timeout Fired at: <br>' + (timeAfter100MS - timeStart) + 'ms<br> (should be ~3000 + ~amount blocked)');
}
function block() {
for (var i = 0; i < 100000000; i++) {};
}
I wrote a small (2-file) library for exactly these purposes : running heavy code whenever the CPU has idle time (using requestAnimationFrame) by splitting the code into smaller iterations so it doesn't block the whole application by allotting a specific percentage of CPU time to execution the code, and use the remainder to execute other scripts / update UI.
It functions similarly to other answers, but might be convenient to you as you can easily calculate elapsed time between executions, if you need to know these figures (or use it to leverage operations within your application, as that's what it was written for)
https://github.com/igorski/zThreader
Related
I'm making a little clock using getTime(), but the output won't refresh. I have tried this:
<p id="clock"></p>
var i;
for (i=0; i < 5; i++){
var date = getTime;
var time = time.getHour() + ":" + time.getMinute() + ":" + time.getSecond()
getElementById('clock').innerHTML = time;
}
Though it just keeps my tab in a constant state of loading. How do I fix this? Thanks!
Just move your code inside setInterval()
let i = 5;
const timer = setInterval(() => {
var date = getTime;
var time = time.getHour() + ":" + time.getMinute() + ":" + time.getSecond()
getElementById('clock').innerHTML = time;
i--;
if (i < 0) {
clearInterval(timer)
}
}, 1000);
So setInterval() lets your code run asyncrounously, that means it doesnt block the rest of the execution, because it runs "seperately" (I would like to describe it better, but my english skills are lacking).
setInterval runs your code every 1000 ms (as specified) forever, unless you dont clear it with clearInterval(). If you want to make a timer it is quiet useful.
this () => {} is an arrow function. You can also use function(){}, if you want, in this case it makes no difference.
Here is the w3schools reference for setInterval(): https://www.w3schools.com/jsref/met_win_setinterval.asp
and here is the MDN reference to asynchronous javascript: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous
Good luck with your project ;)
I hope this helps
setInterval (function refresh(){
var fecha = new Date();
var hora = fecha.getHours();
var minutos = fecha.getMinutes();
var segundos = fecha.getSeconds();
var textoHora = `
${hora}:${minutos}:${segundos}
`;
document.getElementById('clock').innerHTML = textoHora;
}, 1000);
I have created my Game Loop like so:
var lastTime = 0;
var nextTime = 0;
function prepareUpdate(callback) {
currentTime = Date.now();
nextTime = mathMax(lastTime + (1000 / targetFPS), currentTime);
return setTimeout(function prepUpdate() {
callback(lastTime = nextTime);
}, nextTime - currentTime);
};
function callUpdate() {
prepareUpdate(callUpdate);
updateGameLoop();
};
callUpdate();
I am doing it this way because I have run into some issues with RequestAnimationFrame. But Now I am experiencing some lag due to the Garbage Collector kicking in every 5 seconds. I am wondering if the update code is causing this, because I am returning the setTimeout function the way I am.
Is there a way to rewrite this to prevent this behaviour?
For the sake of learning I am prototyping an animate function for all HTMLElements, inspired by jQuery. The animation starts up just fine, but I want it to stop after the requestAnimationFrame's time = the duration given in the function. I am using cancelAnimationFrame inside the animation loop, but it doesn't stop the loop.
HTMLElement.prototype.animate = function(properties,duration){
for(prop in properties){
var last = 0,
fps = 60;
function ani(time){
requestAnimationFrame(ani);
if ((time - last) > fps ){
last = time
console.log(time)
if(time >= (duration*1000)){
window.cancelAnimationFrame(aniLoop)
}
}
}
var aniLoop = requestAnimationFrame(ani)
}
}
The function is called like this
c.animate({"property":"value"},1)
At the core of the problem lies that fact that you're only getting the ID of the first animation frame (the var aniLoop = (...) line) and that's what you're trying to cancel - except that each call to requestAnimationFrame has a different ID, thus you'd need to store the return value of the last call and cancel that instead:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
prop,
last = 0,
fps = 60;
for (prop in properties) {
function ani(time) {
aniLoop = requestAnimationFrame(ani);
if ((time - last) > fps) {
last = time;
console.log(time);
if (time >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
}
aniLoop = requestAnimationFrame(ani);
}
};
There are, however, several other problems with your code that need to be tackled as well, otherwise your function will blow up rather thoroughly:
:1 Function declaration in a loop
I would recommend reading about differences between function declaration and expression to get a better picture, but the problem here is that you're doing function declaration in a loop, which is considered undefined behaviour (some engines will replace the functions, some will not, some will blow up). Given that the animations have only single duration given and thus, are probably synchronised, it'd be a better option to iterate over properties to animate inside of a single animation function, like so:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
last = 0,
fps = 60;
function ani(time) {
var prop;
aniLoop = requestAnimationFrame(ani);
if ((time - last) > fps) {
last = time;
for (prop in properties) {
console.log(prop + ': ' + time);
}
if (time >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
}
aniLoop = requestAnimationFrame(ani);
}
:2 Animation timestamping
As it looks currently, your animation function will probably not run more than one frame anyway - if you look at requestAnimationFrame documentation on MDN, you'll notice that the callback given to requestAnimationFrame is given a timestamp, i.e. value in milliseconds from the beginning of UNIX epoch (1st of January 1970) - therefore the condition of time >= (duration * 1000) will always be true. Instead of that, register the starting time when you kick the animation off and compare the timestamp within the callback to it, like so:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
start,
last = 0,
fps = 60;
function ani(time) {
var prop,
progress;
aniLoop = requestAnimationFrame(ani);
if ((time - last) > fps) {
last = time;
progress = time - start;
for (prop in properties) {
console.log(prop + ': ' + progress + ' out of ' + (duration * 1000));
}
// This is where we get a difference between current and starting time
if (progress >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
}
start = Date.now();
aniLoop = requestAnimationFrame(ani);
}
:3 Animation throttling
This one is not as crucial, but still worth a consideration - requestAnimationFrame is intended to be automatically throttled and regulated by the browser, thus you don't need to apply your own conditions on whether animation should run (it won't go over 60FPS anyway, as that's the specification's ceiling). Instead, it should simply work on difference of current time from starting time, to make sure your animation still ends up in correct place even if for some reason, there is a lag in animation:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
start;
function ani(time) {
var prop,
progress;
aniLoop = requestAnimationFrame(ani);
progress = time - start;
for (prop in properties) {
console.log(prop + ': ' + progress + ' out of ' + (duration * 1000));
}
// This is where we get a difference between current and starting time
if (progress >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
start = Date.now();
aniLoop = requestAnimationFrame(ani);
}
this file
http://www.iguanademos.com/Jare/docs/html5/Lessons/Lesson2/js/GameLoopManager.js
taken from this site
Here is the code:
// ----------------------------------------
// GameLoopManager
// By Javier Arevalo
var GameLoopManager = new function() {
this.lastTime = 0;
this.gameTick = null;
this.prevElapsed = 0;
this.prevElapsed2 = 0;
I understand the declaration of variables,
and they are used to record the time between frames.
this.run = function(gameTick) {
var prevTick = this.gameTick;
this.gameTick = gameTick;
if (this.lastTime == 0)
{
// Once started, the loop never stops.
// But this function is called to change tick functions.
// Avoid requesting multiple frames per frame.
var bindThis = this;
requestAnimationFrame(function() { bindThis.tick(); } );
this.lastTime = 0;
}
}
I don't understand why he uses var bindThis = this
this.stop = function() {
this.run(null);
}
This function set's gameTick to null, breaking the loop in this.tick function.
this.tick = function () {
if (this.gameTick != null)
{
var bindThis = this;
requestAnimationFrame(function() { bindThis.tick(); } );
}
else
{
this.lastTime = 0;
return;
}
var timeNow = Date.now();
var elapsed = timeNow - this.lastTime;
if (elapsed > 0)
{
if (this.lastTime != 0)
{
if (elapsed > 1000) // Cap max elapsed time to 1 second to avoid death spiral
elapsed = 1000;
// Hackish fps smoothing
var smoothElapsed = (elapsed + this.prevElapsed + this.prevElapsed2)/3;
this.gameTick(0.001*smoothElapsed);
this.prevElapsed2 = this.prevElapsed;
this.prevElapsed = elapsed;
}
this.lastTime = timeNow;
}
}
}
Most of this code is what I don't understand, I can see he is recording the time elapsed between frames, but the rest of the code is lost to me.
On the website he uses the term singleton, which is used to prevent the program trying to update the same frame twice?
I have a bit of experience with the javascript syntax, but the concepts of singleton, and the general goal/function of this file is lost to me.
Why is the above code needed instead of just calling
requestAnimationFrame(function() {} );
The reason he uses bindThis is that he is passing a method into an anonymous function on the next line. If he merely used this.tick(), this would be defined as the context of requestAnimationFrame. He could achieve the same thing by using call or apply.
Singletons are classes that are only instantiated once. This is a matter of practice, and not a matter of syntax - javascript doesn't know what a singleton is. By calling it a "Singleton", he is merely communicating that this is a class that is instantiated only once, and everything that needs it will reference the same instance.
I have a fairly long running (3 to 10 second) function that loads data in the background for a fairly infrequently used part of the page. The question I have is what is the optimal running time per execution and delay time between to ensure that the rest of the page stays fairly interactive, but the loading of the data is not overly delayed by breaking it up?
For example:
var i = 0;
var chunkSize = 10;
var timeout = 1;
var data; //some array
var bigFunction = function() {
var nextStop = chunkSize + i; //find next stop
if (nextStop > data.length) { nextStop = data.length }
for (; i < nextStop; i++) {
doSomethingWithData(data[i]);
}
if (i == data.length) { setTimeout(finalizingFunction, timeout); }
else { setTimeout(bigFunction , timeoutLengthInMs); }
};
bigFunction(); //start it all off
Now, I've done some testing, and a chunkSize that yields about a 100ms execution time and a 1ms timeout seem to yield a pretty responsive UI, but some examples I've seen recommend much larger chunks (~300ms) and longer timeouts (20 to 100 ms). Am I missing some dangers in cutting my function into too many small chunks, or is trial and error a safe way to determine these numbers?
Any timeout value less than roughly 15ms is equivalent - the browser will update the DOM, etc and then execute the timeout. See this and this for more info. I tend to use setTimeout(fn, 0).
I would check the time elapsed instead of guessing numbers, because as Jason pointed out, there will be speed differences between clients:
var data; // some array
var i = 0;
var MAX_ITERS = 20; // in case the system time gets set backwards, etc
var CHECK_TIME_EVERY_N_ITERS = 3; // so that we don't check elapsed time excessively
var TIMEOUT_EVERY_X_MS = 300;
var bigFunction = function () {
var maxI = i + MAX_ITERS;
if (maxI > data.length) { maxI = data.length }
var nextTimeI;
var startTime = (new Date()).getTime(); // ms since 1970
var msElapsed;
while (i < maxI) {
nextTimeI = i + CHECK_TIME_EVERY_N_ITERS;
if (nextTimeI > data.length) { nextTimeI = data.length }
for (; i < nextTimeI; i++) {
doSomethingWithData(data[i]);
}
msElapsed = (new Date()).getTime() - startTime;
if (msElapsed > TIMEOUT_EVERY_X_MS) {
break;
}
}
if (i == data.length) {
setTimeout(finalizingFunction, 0);
} else {
setTimeout(bigFunction , 0);
}
};
bigFunction(); //start it all off
The 1ms timeout is not actually 1 ms. By the time the thread yields, to achieve the 1ms, it probably yielded much more - because the typical thread time-slice is 30ms. Any number of other threads may have executed during the 1ms timeout, which could mean that 200ms went by before you received control again.
Why not just have the 'doSomethingWithData' execute in an entirely different thread?