which solution is best performance wise [closed] - javascript

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I am building a small game in javascript to learn object oriented programming in javascript. In it, units move around and do basic stuff.
Animations happens every 20 millisecond. Collision detection should happen every 100 millisecond.
It seems to be faster to have a single setInterval running in the document, as compared to a new setInterval every time a unit is instantiated. So I am doing this. Source: http://www.schillmania.com/content/projects/javascript-animation-2/.
But now I have to do collision detection, as well as animation. I see two solutions:
Solution 1:
window.setInterval(AnimateFunc,20);
function AnimateFunc(){
for (i = 0; i < UnitArray.length; i++){
UnitArray[i].Behaviour(); // Every unit in my array of units behaviour function is called. ex: add 2 pixels to unit.style.top.
}
CollisionDetectionCounter += 1; // Globale variable
if (CollisionDetectionCounter === 5){
CollisionDetectionFunc();
CollisionDetectionCounter = 0;
}
}
With this solution the single setInterval call in the document is preserved. But every second 50 variable incrementations and 50 comparisons is made.
Solution 2:
window.setInterval(AnimateFunc,20);
window.setInterval(CollisionDetectionFunc, 100);
function AnimateFunc(){
for (i = 0; i < UnitArray.length; i++){
UnitArray[i].Behaviour();
}
}
function CollisionDetectionFunc(){
// Perform collision detection between all units curently on the field.
}
With this solution we prevent the 50 incrementations/comparisons but there is now two setInterval functions in play.
So which solution would be best, do you think?
I realize it probably won't matter much. I will probably make other decisions that will screw my game up way worse performance wise down the line, but I am curious :)
Thank you in advance.

Unless those exact timing constraints are your hard limits, I would suggest requestAnimationFrame instead of setInterval. But if you must use setInterval, then I would recommend an approach that's a hybrid of your two setups:
function AnimateFunc(){
for (i = 0; i < UnitArray.length; i++){
UnitArray[i].Behaviour();
}
}
function CollisionDetectionFunc(){
// Perform collision detection between all units curently on the field.
}
function TickFunc() {
AnimateFunc();
CollisionDetectionCounter += 1; // Globale variable
if (CollisionDetectionCounter === 5){
CollisionDetectionFunc();
CollisionDetectionCounter = 0;
}
}
window.setInterval(TickFunc, 20);
This lets you control things off of a single timer. That's not so much a performance concern as a syncing concern: multiple timers can drift relative to one another, and it doesn't sound like you want this to happen. But it also lets you maintain separation of concerns: there's a function for animation, a function for collisions, and a function that coordinates them.
This can also be adapted to use requestAnimationFrame.

You should use requestAnimationFrame instead, for more Information see:
CSS-Tricks
MDN
requestAnimationFrame for Smart Animating
Your code could look similar to this:
var lastCollisionDetection;
var requestId;
function animate(time) {
if (lastCollisionDetection + 100 > time) {
lastCollisionDetection = time;
// TODO: collision detection
}
// TODO: update objects - you might want to check the passed time here as well
requestId = window.requestAnimationFrame(animate);
}
lastCollisionDetection = window.performance.now();
requestId = window.requestAnimationFrame(animate);

Related

Performence: When animating using requestAnimationFrame, is an interval better than manually calculating the ellasped time?

In examples I see two different ways how to handle animations using requestAnimationFrame():
The first one using setTimeout()
const e = document.getElementById('e');
let count = 0;
function move(timestamp) {
e.style.left = ++count + 'px';
setTimeout(f=>{
requestAnimationFrame(move);
}, 200);
};
requestAnimationFrame(move);
Try it in this jsfiddle.
The second one by calulating the ellapsed time yourself
const e = document.getElementById('e');
let count = 0;
let past = null;
function move(timestamp) {
if (past !== null && timestamp - past < 200) {
requestAnimationFrame(move);
return;
}
past = timestamp;
e.style.left = ++count + 'px';
requestAnimationFrame(move);
};
requestAnimationFrame(move);
Try it in this jsfiddle.
Now my question is: Which one performs better?
My guess is, that if an interval is applicable, it will perform better. There aren't so many logical expressions to evaluate and so many calculations to do. But that's just a guess. So, are their any benefits of one way over the other?
You should do the first one. However you shouldn't assume it will be exactly 200ms. If you need to know exactly how many milliseconds it's been, you must calculate it every time like in your second example, but keep using the structure of your first example.
The problem with the second example is that it calculates the new frames just as often as the first example, but it runs the function just to check that it doesn't need to run the function every other time it runs. Lots of wasted computation there.
You need to calculate the time elapsed (if you need accuracy) because the setTimeout combined with the requestAnimationFrame will not be guaranteed to run in 200ms. The requestAnimationFrame in particular will add time onto that if it feels it needs to before it's ready to give you permission to animate (which is basically what requestAnimationFrame is -- it's giving you permission to animate, saying 'I'm ready now').

Countdown in seconds: milliseconds [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I need a div's text to turn red when you start counting, it should lasts 3 seconds and leave the red text while counting. Then return back to normal when it finishes (and regular displays when stopped 3 seconds)
Displaying the count in milliseconds.
I tried, but it still fails.
Something like this?
var foo = document.getElementById('foo');
function startCountDown(ele) {
ele.origText = ele.innerHTML;
ele.style.color = "red";
ele.timeStart = (new Date()).getTime() + 3000;
ele.intervalVar = setInterval(function() {
var curTime = ele.timeStart - (new Date()).getTime();
if(curTime < 0) {
ele.innerHTML = ele.origText;
ele.style.color = "";
clearInterval(ele.intervalVar);
}
else ele.innerHTML = curTime;
},20);
}
setTimeout(function() {startCountDown(foo);},1000);
<div id = 'foo'>bar</div>
Innovative/Alternative way to show timers using requestAnimationFrame,
http://jsfiddle.net/wjtkr95t/4/
wich has less impact to the browser's memory.It also allows you with some minor modifications to have more control over timers,animations ... whatever...
var
end,
now=Date.now,
raf=window.requestAnimationFrame,
duration=120000,//MS
out=document.getElementById('out');
function displayTime(){
var c=end-now();
out.textContent=ms2TimeString(c>0?(raf(displayTime),c):0);
}
function go(){
end=now()+duration;
raf(displayTime);
}
Ms to timeString function
function ms2TimeString(a,k,s,m,h){
return k=a%1e3,
s=a/1e3%60|0,
m=a/6e4%60|0,
h=a/36e5%24|0,
(h?(h<10?'0'+h:h)+':':'')+
(m<10?0:'')+m+':'+
(s<10?0:'')+s+'.'+
(k<100?k<10?'00':0:'')+k
}
https://codereview.stackexchange.com/q/45335/33435
DEMO
http://jsfiddle.net/wjtkr95t/
DEMO (change the color) .. only 100ms.. change it
http://jsfiddle.net/wjtkr95t/1/
DEMO (change the color after 2 sec) .. only 100ms.. change it
http://jsfiddle.net/wjtkr95t/2/
DEMO (change the color after 2 sec with 700ms color animation)
http://jsfiddle.net/wjtkr95t/3/
if you have any questions just ask
I think if you show us your algorithm it would be clearer for anyone who want to help you. Waiting it, I would recommend you to see how the function setInterval() works, because it puts a delay so you probably would use it to reach your goal.
Go here to see docs.
This could be done using CSS animations, and adding a class to a div element.
This method would be better, since CSS animations are not blocking, whereas since javascript is single synchronous thread (for the most part); and so using setInterval will block the rest of the javascript from functioning.

Cross-browser way of avoiding unresponsive script [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 8 years ago.
Improve this question
I would like to avoid unresponsive javascript in all browsers.
Is it possible to write code with that in mind?
DETAILS: Problem is that currently there is a potential script block that executes fine in Chrome on my PC, but causing problems on IE (various versions). The worst thing is that I really don't know for sure if it is that script block at all. I will rewrite and solve that. However, I would like to know what exactly I should be avoiding while coding. This...
http://www.sitepoint.com/javascript-execution-browser-limits/
...is an interesting read but it's too general.
EDIT: I'm using jQuery/jQueryUI as well.
EDIT 2: There are patterns/principles to use to avoid particular problems. E.g. singleton pattern, PRG pattern, DRY principle... and such. Is there something like that for this kind of problem?
I've run into problems like this before as well.
The thing to keep in mind as you code, is where does my code begin execution, and where does my code end execution. For all of the time between those two points, the browser's UI thread is blocked, and the browser makers have understandably developed counter measures.
As far as what to avoid, avoid long, continuous loops.
Here's an extreme example:
function howManyMultiplesOfFourBelow(foo) {
var i = 0, j = 0;
while (i < foo) {
i++;
if (i % 4 === 0) {
j++;
}
}
return j;
}
If you pass 10,000,000 to that function, IE will definitely throw a fit. There is more than one way to program around this kind of situation; what I prefer is to break up the code using setTimeout/setInterval. After setting an interval and returning out of a function, we release the UI thread back to the browser, and the browser is in charge of executing the interval as often as we've requested (or as often as it is able).
I combine this with Futures/Promises; In particular, jQuery's implementation.
Using this style, the above example could be rewritten not to block the UI thread during the calculation by leveraging promises, and setInterval.
function howManyMultiplesOfFourBelow(foo) {
var deferred = $.Deferred(),
interval,
i = 0,
j = 0;
interval = setInterval(function () {
if (i >= foo) {
clearInterval(interval);
deferred.resolve(j);
return;
}
i++;
if (i % 4 === 0) {
j++;
}
}, 1);
return deferred.promise();
}
The first important difference is that this function no longer returns the answer, but instead a promise of an answer. So, consuming code might look like this:
howManyMultiplesOfFourBelow(10000000).done(function (multiples) {
//Update the DOM with the answer (multiples)
});
Returning to your question more generally, think about how much of your code must be run continuously, and if any of it could be delayed, or broken up in order to release the UI thread briefly.

Improve performance in js `for` loop

So I'm looking for some advice on the best method for toggling the class (set of three) of an element in a loop ending at 360 iterations. I'm trying to avoid nested loops, and ensure good performance.
What I have:
// jQuery flavour js
// vars
var framesCount = '360'; // total frames
var framesInterval = '5000'; // interval
var statesCount = 3; // number of states
var statesCountSplit = framesInterval/statesCount;
var $scene = $('#scene');
var $counter = $scene.find('.counter');
// An early brain dump
for (f = 1; f < framesCount; f += 1) {
$counter.text(f);
for (i = 1; i < statesCount; i += 1) {
setTimeout(function() {
$scene.removeClass().addClass('state-'+i);
}, statesCountSplit);
}
}
So you see for each of 360 frames there are three class switchouts at intervals. Although I haven't tested I'm concerned for the performance hit here once that frames value goes into the thousands (which it might).
This snippet is obviously flawed (very), please let me know what I can do to make this a) work, b) work efficiently. Thanks :-)
Some general advice:
1) Don't declare functions in a loop
Does this really need to be done in a setTimeout?
for (i = 1; i < statesCount; i += 1) {
setTimeout(function() {
$scene.removeClass().addClass('state-'+i);
}, statesCountSplit);
}
2) DOM operations are expensive
Is this really necessary? This will toggle so fast that you won't notice the counter going up. I don't understand the intent here, but it seems unnecessary.
$counter.text(f);
3) Don't optimize early
In your question, you stated that you haven't profiled the code in question. Currently, there's only about 1000 iterations, which shouldn't be that bad. DOM operations aren't too bad, as long as you aren't inserting/removing elements, and you're just modifying them.
I really wouldn't worry about performance at this point. There are other micro-optimizations you could apply (like changing the for loop into a decrementing while loop to save on a compare), but you gave no indication that the performance is a problem.
Closing thoughts
If I understand the logic correctly, you're code doesn't match it. The code will currently increment .counter as fast as the processor can iterate over your loops (should only take a few milliseconds or so for everything) and each of your "class switchouts" will fire 360 times within a few milliseconds of each other.
Fix your logic errors first, then worry about optimization if it becomes a problem.
Don't use a for loop for this. This will generate lots of setTimeout events which is known to slow browsers down. Use a single setTimeout instead:
function animate(framesCount, statesCount) {
$scene.removeClass().addClass('state-'+statesCount);
if (framesCount) {
setTimeout(
function(){
animate(framesCount-1,(statesCount%3)+1);
},
statesCountSplit
);
}
}
animate(360*3,1);

CSS transitions blocked by JavaScript

I am trying to create a loading bar during a very intensive period of JavaScript where some pretty heavy 3d arrays are built and filled. This loading bar needs to remain empty until the user clicks a button.
The freezing occurs whether or not I'm using -webkit-transition (This app can be chrome exclusive, cross browser is not necessary in my case).
Seeking simplicity I've built my bar like this...
<div id="loader">
<div id="thumb">
</div>
</div>
... and then sought to increment that bar at various stages of my main for loop:
for(i = 0; i < 5 ; i++){
document.getElementById('thumb').style.width = i*25 + '%';
//More Code
}
Problem is that everything freezes until the JavaScript finishes.
I found a similar question on Stack Overflow, Using CSS animation while javascript computes, and in the comments found and considered and/or tried the following:
Web Workers
Don't think it'll work since my script is filling an array with objects and constructors containing functions which according to this site isn't going to work
jQuery
Not an option, I can't use external libraries in my app - in any case, importing a whole library just for a loading bar seems kind of like overkill...
Keyframes
This was promising and I tried it, but in the end it freezes also, so no joy
timeOut()s
Thought about this, but since the point of the loading bar is to reduce frustration, increasing the waiting time seems counter-productive
I'd be happy to have any incrementation of the bar at this stage, even if it's not smooth! I'm pretty sure this is a problem that has struck more than just me - maybe someone has an interesting solution?
P.S.: I'm posting this as a new question rather than adding to the referenced question since I'm specifically seeking help with JavaScript (not jQuery) and would prefer if I could get it using a transition (!=animation) on the width.
Some people already mentioned that you should use timeouts. That's the appropriate approach, bc it'll give the browser time to "breathe" and render your progress bar mid-task.
You have to split your code up to work asynchronously. Say you currently have something like this:
function doAllTheWork() {
for(var i = 0; i < reallyBigNumberOfIterations; i++) {
processorIntensiveTask(i);
}
}
Then you need to turn it into something like this:
var i = 0;
function doSomeWork() {
var startTime = Date.now();
while(i < reallyBigNumberOfIterations && (Date.now() - startTime) < 30) {
processorIntensiveTask(i);
i++;
}
if(i < reallyBigNumberOfIterations) {
// Here you update the progress bar
incrementBar(i / reallyBigNumberOfIterations);
// Schedule a timeout to continue working on the heavy task
setTimeout(doSomeWork, 50);
}
else {
taskFinished();
}
}
function incrementBar(fraction) {
console.log(Math.round(fraction * 100) + ' percent done');
}
function taskFinished() { console.log('Done!'); }
doSomeWork();
Note the expression (Date.now() - startTime) < 30. That means the loop will get as much done as it can in the span of 30 milliseconds. You can make this number bigger, but anything over 100ms (essentially 10 frames-per-second) is going to start feeling sluggish from the user's point of view.
It may be true that the overall task is going to take somewhat longer using this approach as opposed to the synchronous version. However, from the user's experience, having an indication that something is happening is better than waiting indefinitely while nothing seems to be happening – even if the latter wait time is shorter.
Have you tried going even simpler and making a function, let's say:
Pseudo:
Function increment_bar(amount = 10)
{
document.getElementById('thumb').style.width = i*amount + '%';
}
Then from wherever you are doing your processing work just calling that function every x seconds or whenever you hit a certain point in processing (let's say 10-20% completion?)
Pseudo:
{
Doing_work_here;
increment_bar(25);
LOOP
}

Categories