is setInterval slowing down my site - javascript

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

Related

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.

How can I make my setTimout functions run at the same speed?

Preface: I have a demo of the problem on my personal site (I hope this is ok. If not, I can try to set it up on jsfiddle). I'm intending this question to be a little fun, while also trying to understand the time functions take in javascript.
I'm incrementing the value of progress bars on a timeout. Ideally (if functions run instantaneously) they should fill at the same speed, but in the real world, they do not. The code is this:
function setProgress(bar, myPer) {
bar.progressbar({ value: myPer })
.children('.ui-progressbar-value')
.html(myPer.toPrecision(3) + '%')
.attr('align', 'center');
myPer++;
if(myPer == 100) { myPer = 0; }
}
function moveProgress(bar, myPer, inc, delay){
setProgress(bar, myPer);
if(myPer >= 100) { myPer = 0; }
setTimeout(function() { moveProgress(bar, myPer+inc, inc, delay); }, delay);
}
$(function() {
moveProgress($(".progressBar#bar1"), 0, 1, 500);
moveProgress($(".progressBar#bar2"), 0, 1, 500);
moveProgress($(".progressBar#bar3"), 0, .1, 50);
moveProgress($(".progressBar#bar4"), 0, .01, 5);
});
Naively, one would think should all run (fill the progress bar) at the same speed.
However, in the first two bars, (if we call "setting the progress bar" a single operation) I'm performing one operation every 500 ms for a total of 500 operations to fill the bar; in the third, I'm performing one operation every 50ms for a total of 5,000 operations to fill the bar; in the fourth, I'm performing one operation every 5ms for a total of 50,000 operations to fill the bar.
What part of my code is takes the longest, causes these speed differences, and could be altered in order to make them appear to function in the way that they do (the fourth bar gets smaller increments), but also run at the same speed?
The biggest problem with using setTimeout for things like this is that your code execution happens between timeouts and is not accounted for in the value sent to setTimeout. If your delay is 5 ms and your code takes 5 ms to execute, you're essentially doubling your time.
Another factor is that once your timeout fires, if another piece of code is already executing, it will have to wait for that to finish, delaying execution.
This is very similar to problems people have when trying to use setTimeout for a clock or stopwatch. The solution is to compare the current time with the time that the program started and calculate the time based on that. You could do something similar. Check how long it has been since you started and set the % based on that.
What causes the speed difference two things: first is the fact that you executing more code to fill the bottom bar (as you allude to in the 2nd to last paragraph). Also, every time you set a timeout, your browser queues it up... the actual delay may be longer than what you specify, depending on how much is in the queue (see MDN on window.setTimeout).
Love the question, i don't have a very precise answer but here are my 2 cents:
Javascript is a very fast language that deals very well with it's event loop and therefore eats setTimeouts and setIntervals for breakfast.
There are limits though, and they depend on a large number of factors, such as browser and computer speed, quantity of functions you have on the event loop, complexity of the code to execute and timeout values...
In this case, i think it's obvious that if you try to execute one function every 500ms, it is going to behave a lot better than executing it every 50ms, therefore a lot better than every 5ms. If you take into account that you are running them all on top of each other, you can predict that the performance will not be optimal.
You can try this exercise:
take the 500ms one, and run it alone. mark the total time it took to fill the bar (right here you will see that it's going to take a little longer than predicted).
try executing two 500ms timeouts at the same time, and see that the total time just got a bit longer.
If you add the 50ms to it, and then the 5ms one, you will see that you will lose performance everytime...

Issue with a javascript jQuery script in OpenX

I'm using OpenX at work, and one of my boss requirements is a expandable banner. For that (and made a horrible simplification of the whole story) I made this script.
function retro(){
var acs = jQuery('#trial_center').height() - 5;
jQuery('#trial_center').css('height', acs + 'px');
}
jQuery(document).ready(function(){
jQuery("#trial_center").mouseover(function(){
setTimeout("jQuery('#trial_center').css('height', '500px')", 1000);
})
jQuery("#trial_center").mouseleave(function(){
var c = 89;
while (c > 0) {
setTimeout("retro()", 1000);
c--;
}
})
});
The problem I have is in the mouseleave event: the original idea was to made this loop several times (89 times), and each time, decrease the height of the banner until it get his original size. Why this? Because my boss want an "effect", and this effect must be in sync with the customer's flash.
The problem is that instead of decrease his size progressively, apparently the script made all the operations an "after" the sum of setTimeout calls, updated the page. So, the result is exactly as the banner shrinks one time from the expanded size to the original size.
I don't know what is wrong with this, or if exists other more intelligent solution.
Any help will be very appreciate.
Thanks in advance
Your loop setting the timeout is just setting 89 timers for one second later than the loop runs, and the loop will run in milliseconds — so they'll all fire about a second later. That doesn't sound like what you want to do.
Two options for you:
1. Use animate
jQuery's animate function seems like it does what you want. You can tell jQuery to animate the size change, and you tell it how long to take to do so:
jQuery('#trial_center').animate({
height: "500px" // Or whatever the desired ending height is
}, 1000);
That will animate changing the height of the container from whatever it is at the point that code runs to 500px, across the course of 1,000 milliseconds (one second). Obviously you can change the duration to whatever you like.
2. Set up the timer loop manually
If for whatever reason you don't want to use animate, you can do this manually (of course you can; jQuery can't do anything you can't do yourself, it just makes things easier). Here's how to set up a timer loop:
jQuery("#trial_center").mouseleave(function(){
var c = 89;
// Do the first one right now, which will schedule the next
iteration();
// Our function here lives on until all the iterations are
// complete
function iteration() {
// Do one
retro();
// Schedule this next unless we're done
if (--c > 0 {
setTimeout(iteration, 100); // 100ms = 1/10th second
}
}
});
That works because iteration is a closure over c (amongst other things). Don't worry about the term "closure" if it's unfamiliar, closures are not complicated.
Separately: You're using mouseover to set the height of the trial_center element a second later; you probably wanted mouseneter rather than mouseover. mouseover repeats as the mouse moves across it.
Off-topic:
It's best not to use strings with setTimeout; just pass it a function reference instead. For example, instead of
setTimeout("retro()", 1000);
you'd use
setTimeout(retro, 1000); // No quotes, and no ()
And for the other place you're using, instead of
setTimeout("jQuery('#trial_center').css('height', '500px')", 1000);
you'd use
setTimeout(function() {
jQuery('#trial_center').css('height', '500px');
}, 1000);

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

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)

Categories

Resources