Why do multiple setTimeout() calls cause so much lag? - javascript

I have a complex animation sequence involving fades and transitions in JavaScript. During this sequence, which consists of four elements changing at once, a setTimeout is used on each element.
Tested in Internet Explorer 9, the animation works at realtime speed (it should take 1.6 seconds and it took exactly 1.6 seconds). ANY other browser will lag horribly, with animation times of 4 seconds (Firefox 3 and 4, Chrome, Opera) and something like 20 seconds in IE 8 and below.
How can IE9 go so fast while all other browsers are stuck in the mud?
I have tried to find ways of merging the elements into one, so as to one have one setTimeout at any given time, but unfortunately it wouldn't stand up to any interference (such as clicking a different link to start a new animation before the current one has finished).
EDIT: To elaborate in response to comments, here's the outline of the code:
link.onclick = function() {
clearTimeout(colourFadeTimeout);
colourFadeTimeout = setTimeout("colourFade(0);",25);
clearTimeout(arrowScrollTimeout);
arrowScrollTimeout = setTimeout("arrowScroll(0);",25);
clearTimeout(pageFadeOutTimeout);
pageFadeOutTimeout = setTimeout("pageFadeOut(0);",25);
clearTimeout(pageFadeInTimeout);
pageFadeInTimeout = setTimeout("pageFadeIn(0);",25);
}
Each of the four functions progress the fade by one frame, then set another timeout with the argument incremented, until the end of the animation.
You can see the page at http://adamhaskell.net/cw/index.html (Username: knockknock; Password: goaway) (it has sound and music, which can be disabled, but be warned!) - my JavaScript is very messy since I haven't really organised it properly, but it is commented a bit so hopefully you can see what the general idea is.

Several things:
Your timeout is 25ms. This translates to 40fps which is a very high framerate to try to achieve via javascript. Especially for things involving DOM manipulation that may trigger reflows. Increase it to 50 or 60. 15fps should be more than fluid enough for the kinds of animation you're doing. You're not trying to display videos here, just move things around the page.
Don't use strings as the first parameter to setTimeout(). Especially if you care about performance. That will force javascript to recompile the string each frame of animation. Use a function instead. If you need to pass an argument use an anonymous function to wrap the function you want to execute:
setTimeout(function(){
pageFadeIn(0)
},50);
this will only get compiled once when the script is loaded.
As mentioned by Ben, it is cheaper to use a single setTimeout to schedule the functions. For that matter, code clarity may improve by using setInterval instead (or it may not, depends on your coding style).
Additional answer:
Programming javascript animation is all about optimisation and compromise. It's possible to animate lots of things on the page with little slow-down but you need to know how to do it right and decide what to sacrifice. As an example of just how much can be animated at once is a demo real-time strategy game I wrote a couple of years ago.
Among the things I did to optimize the game are:
The walking soldiers are made up of only two frames of animation and I simply toggle between the two images. But the effect is very convincing nonetheless. You don't need perfect animation, just one that looks convincing.
I use a single setInterval for everything. It's cheaper CPU-wise and easier to manage. Just decide on a base frame rate and then schedule for different animation to start at different times.

Well, that's a lot of javascript (despite the "quadruple-dose of awesomeness" :)
You're firing a lot of setTimeout sequence, I'm not sure how well JS engines are optimised for this.. particularly IE <= 8
Ok, maybe just a rough suggestion... You could maybe write a small timing engine.
Maintain a global object that stores your current running timed events with the function to run, and the delay...
Then have a single setTimeout handler that check against that global object, and decreases the delay at each iteration and call the function when the delay becomes < 0
you global event would looks something like that:
var events = {
fade1 : {
fn : func_name,
delay : 25,
params : {}
}
fadeArrow : {
fn : func_name,
delay : 500,
params : {}
}
slideArrow : {
fn : func_name,
delay : 500,
params : {
arrow:some_value
}
}
}
then create a function to loop through these at an interval (maybe 10 or 20 ms) and decrease your delays until they complete and fire the function with params as a paramer to the function (check Function.call for that).
Once down, fire setTimeout again with the same delay..
To cancel an event just unset the property from the events list..
Build a few method to add / remove queued events, update params and so on..
That would reduce everything to just one timeout handler..
(just an idea)

Related

is setInterval slowing down my site

I want to know if setInterval slowing down my site or not?
setInterval(function(){
var uploadbtndiv = document.getElementById("imagesmaindiv");
if (uploadbtndiv.childElementCount == 1) {
document.getElementsByClassName("plusupload")[0].style.top = "17px";
}else{
document.getElementsByClassName("plusupload")[0].style.top = "-81px";
}
}, 10);
setInterval doesn't slow down your site. Using it incorrectly can. In your code, you're scheduling an operation to happen roughly every 10ms. That's a lot. Even an efficient operation (and yours is tolerably efficient, though it could be more so) done 100 times a second can add up.
You probably don't want setInterval in your example. You appear to want to change where something is depending on how many elements there are in imagesmaindiv. I'd probably do that one of three different ways:
By putting that if/else in the code that adds/removes elements to/from imagesmaindiv
By using CSS, but it depends on the structure
By using a mutation observer on imagesmaindiv, so I only do the work when its contents change instead of 100 times a second

Waiting for animations in a SetInterval gameloop

So my problem is I want to insert a custom animation but I don't want to ruin my gameloop.
My initial gameloop is stated here:
function init(){
if(!gameOver){
if(resetInterval>-1) clearInterval(resetInterval);
createBlock();
resetInterval = setInterval(moveDownCheck,gameSpeed);
}
}
My game is a tetris like game except instead of dropping tetriminos, I drop 2x1 blocks of different color. The moveDownCheck method checks if there are any blocks under my 2x1 block and then drops it by 1 row. This works fine until I have a block hanging without a block underneath since the 2x1 blocks are connected. I want to insert a drop animation that would take about a second and drop the hanging block by the same gameSpeed increment.
Here is my attempt that doesn't work:
function moveFallingDown(){
fbDownFlag = false;
clearInterval(resetInterval);
fbInterval = setInterval(function(){
fallingBlock.row++;
console.log("Dropped One Row");
},gameSpeed);
while(landscape[fallingBlock.row+1][fallingBlock.col]==0){
console.log("Waiting to Drop Falling Block");
}
clearInterval(fbInterval);
resetInterval = setInterval(moveDownCheck,gameSpeed);
}
Here I am attempting to wait for the function(){fallingBlock.row++;}, but my game just crashes and in the console "Dropped One Row" yet "Waiting to Drop Falling Block" will display thousands of times.
I guess I shouldn't be using a while loop here, but the only other solution I can think of would be a complete rework of my design, or nested setInterval methods which would just make my head hurt too much.
You can't do this with a while loop, you need to use a recursive function. window.setTimeout would work, however this seems like a good use case for requestAnimationFrame. Check it out here: https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame
You can use that to call your moveFallingDown method, and check how long has passed since the last animation frame to move your animation the right amount according to the game speed, by using the high precision timestamp passed to the requestAnimationFrame callback.
#Adrien Delessert's advice is correct but I'll just add that you're definitely confusing setTimeout and setInterval.
First of all you don't need a while loop.
setInterval IS a loop. So, if you wanted to use it you'd need to wrap (basically) the whole game in a method that moves the game forward (whatever that means) and pass that to setInterval.
However, what you're doing (and this is actually not a terrible approach) is to use it as a means to animate specific things. In that case you should have a recursive(ish) function that keeps calling setTimeout when it's done, if the conditions for another round of animation are met.
I haven't ever used requestAnimationFrame, but that does sound like a much more elegant approach to the problem.
The reason it's better is that it leverages the browser's own refresh timer (about 60 times per second) and will slot your animation frames in along with its own refresh.
So yes, you will listen for that callback and then react to it as necessary. If 60x per second is too fast, you'll need to put in a % based counter for how many of those frames you wish to actually react to.

Most efficient way to throttle continuous JavaScript execution on a web page

I'd like to continuously execute a piece of JavaScript code on a page, spending all available CPU time I can for it, but allowing browser to be functional and responsive at the same time.
If I just run my code continuously, it freezes the browser's UI and browser starts to complain. Right now I pass a zero timeout to setTimeout, which then does a small chunk of work and loops back to setTimeout. This works, but does not seem to utilize all available CPU. Any better ways of doing this you might think of?
Update: To be more specific, the code in question is rendering frames on canvas continuously. The unit of work here is one frame. We aim for the maximum possible frame rate.
Probably what you want is to centralize everything that happens on the page and use requestAnimationFrame to do all your drawing. So basically you would have a function/class that looks something like this (you'll have to forgive some style/syntax errors I'm used to Mootools classes, just take this as an outline)
var Main = function(){
this.queue = [];
this.actions = {};
requestAnimationFrame(this.loop)
}
Main.prototype.loop = function(){
while (this.queue.length){
var action = this.queue.pop();
this.executeAction(e);
}
//do you rendering here
requestAnimationFrame(this.loop);
}
Main.prototype.addToQueue = function(e){
this.queue.push(e);
}
Main.prototype.addAction = function(target, event, callback){
if (this.actions[target] === void 0) this.actions[target] = {};
if (this.actions[target][event] === void 0) this.actions[target][event] = [];
this.actions[target][event].push(callback);
}
Main.prototype.executeAction = function(e){
if (this.actions[e.target]!==void 0 && this.actions[e.target][e.type]!==void 0){
for (var i=0; i<this.actions[e.target][e.type].length; i++){
this.actions[e.target][e.type](e);
}
}
}
So basically you'd use this class to handle everything that happens on the page. Every event handler would be onclick='Main.addToQueue(event)' or however you want to add your events to your page, you just point them to adding the event to the cue, and just use Main.addAction to direct those events to whatever you want them to do. This way every user action gets executed as soon as your canvas is finished redrawing and before it gets redrawn again. So long as your canvas renders at a decent framerate your app should remain responsive.
EDIT: forgot the "this" in requestAnimationFrame(this.loop)
web workers are something to try
https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers
You can tune your performance by changing the amount of work you do per invocation. In your question you say you do a "small chunk of work". Establish a parameter which controls the amount of work being done and try various values.
You might also try to set the timeout before you do the processing. That way the time spent processing should count towards any minimum the browsers set.
One technique I use is to have a counter in my processing loop counting iterations. Then set up an interval of, say one second, in that function, display the counter and clear it to zero. This provides a rough performance value with which to measure the effects of changes you make.
In general this is likely to be very dependent on specific browsers, even versions of browsers. With tunable parameters and performance measurements you could implement a feedback loop to optimize in real-time.
One can use window.postMessage() to overcome the limitation on the minimum amount of time setTimeout enforces. See this article for details. A demo is available here.

JavaScript setInterval Function Alternative

I just developed an JS game which has too many setIntervals to function calls. The intervals are creating the flicker effect which is not acceptable. I wanted to check with experts if can I anyhow make it smooth adopting alternative to setInterval calls which are smooth too?
Have a look at the game http://umairashraf.net23.net/booble/
You have no "recursive calls". You have a lot of timers being created. For each new bubble you create a new timer! A hundred of callback functions are being fired every 20 ms! And within each of these calls you do
$(bubble).offset().top;
if (pos >= 0 - $(bubble).height()) {
$(bubble).css({ top: (pos - 1000) + "px" });
}
A jquery object is constructed (3 times!), its offset is calculated, its height is queried... A hundred of times, every 20 ms!
For each created bubble you should cache its jquery object and its current position. Keep them in arrays or hashes: bubbleJqueryObjects and bubblePositions. Make a single animateBubbles() callback, which will just update "top" css property of all existing bubbles at once. Save 5000 function calls, 15000 jquery object constructions and 5000 position\size queries. Every second.
You might look into the Composite design pattern. Its a popular design pattern for handling hierarchical type data and being able to query it in a fast way.
I suspect your performance problem is that for every bubble, you create a new animator function that executes every 20ms. That is a lot of functions executing frequently.
You could improve performance by keeping all the bubbles in one data structure, and using one function, executing every 20ms, to update all of them at once.

Understanding how alert() impacts browser event loop

I'm considering adding an alert() to our Javascript utility assert function.
We're an ajax-heavy application, and the way our framework (Ext) implements ajax by polling for the ajax response with setInterval instead of waiting for readystate==4, causes all of our ajax callbacks to execute in a setInterval stack context -- and an exception/assert blowing out of it usually fails silently.
How does a low-level alert() impact the browser event loop? The messagebox by definition must allow the win32 event loop to pump (to respond to the mbox button). Does that mean other browser events, like future setIntervals generated by our framework, resize events, etc, are going to fire? Can this cause trouble for me?
IIRC: you can use Firefox2 and Firefox3.5 to see the difference I'm talking about.
alert('1');
setTimeout(function(){alert('2');}, 10);
alert('3');
Firefox3.5 shows 1-3-2. Firefox2[1] shows 1-2&3 (2 and 3 stacked on top of each other simultaneously). We can replicate 1-2&3 in IE8 with a win32 mbox launched from ActiveX as well, instead of an alert, which wreaked havoc on us back in the day, and I want to make sure we don't go down that path again.
Can anyone point me to specific low level resources that explain this behavior, what the expected behavior is here, and what exactly is going on at a low level, including why the behavior changed across Firefox versions?
[1] you can replicate this on Spoon.net which I can't get working right now. I just reproduced it in a VM with Firefox 2.0.0.20.
First, timers in javascript are not very precise. Intervals smaller than 30ms might be considered all the same, and implementations vary. Don't rely on any implicit ordering.
An alert() will always halt the event loop. If an event or timer fires during the alert, they will be queued and called after the event loop resumes (the alert box is closed).
Take this example:
var hello = document.getElementById('hello')
setTimeout(function(){
hello.style.backgroundColor = 'lime'
}, 5000)
alert('Stop!')
setTimeout(function(){
hello.innerHTML = 'collaborate'
}, 20)
setTimeout(function(){
hello.innerHTML = 'listen'
}, 1000)
There are two possible outcomes:
You close the alert box in under 5 seconds. The two timers that follow will be set and fire at specified intervals. You can see that the event loop is halted because regardless of how long you wait to close the alert, "listen" will always take 1s to execute.
You take longer than 5 seconds to close the alert. The first interval (bgColor) will have passed, so it executes immediately, followed by the two timers being set and called.
http://jsbin.com/iheyi4/edit
As for intervals, while the event loop is stopped it also "stops time", so in this case:
i = 0
setInterval(function(){
document.getElementById('n').innerHTML = ++i
}, 1000)
setTimeout(function(){
alert('stop')
}, 5500)
Regardless of how long you take to close the alert, the next number will always be 6 - the setInterval won't fire multiple times.
http://jsbin.com/urizo6/edit
I haven't been able to replicate the 1-2&3 case, but here is a fiddle that may help you debug what is going on in the different browsers.

Categories

Resources