1

I have an array of objects, I loop through this array one object at a time and I make a couple of checks to see if each object in that array meets certain criteria, if that object meets this criteria I then copy a property of this object in to an array (that property also contains another object).

for(var v = 0; features.length > v; v++){
   var feature = features[v];
    //If the feature is on the map then we want to add it
    if(feature.onScreen()){     
        dataArray.push(feature.attributes); 
    }
}

Now for some reason if this array of objects is big (5000+) in size this operation becomes very expensive and the browser just locks up for a second or two (Some times more).

I cant go in to much more info of what the code does but I was wondering given this loop, what would be the best way to give the browser a break lets say every 500 iterations and so it doesn't lock up and then continue on etc.

Thanks

SomeShinyObject
  • 7,581
  • 6
  • 39
  • 59
Mo.
  • 40,243
  • 37
  • 86
  • 131
  • 1
    Split the array in chunks of a thousand objects each and use `setTimeout()` to handle the next chunk. – nalply Mar 27 '13 at 10:49
  • possible duplicate of [Prevent JavaScript from locking up browser on big loop](http://stackoverflow.com/questions/14063822/prevent-javascript-from-locking-up-browser-on-big-loop) – nalply Mar 27 '13 at 10:51
  • Would be interesting to see whats hidden behind `onScreen`. Maybe you can further improve the performance by optimizing this function. Why can't you go in to much info? – Marcel Gwerder Mar 27 '13 at 10:51
  • 1
    Web workers are only available to a limited number of browsers...who am I kidding...they don't work in IE9 and below, that's about it – SomeShinyObject Mar 27 '13 at 10:52
  • @Christopher - And doing a check for the availabilty of webworkers, and using them where available, would speed this up to where it's no longer noticeable in all browsers but IE. – adeneo Mar 27 '13 at 10:54
  • @adeneo it could be encouragement for users to change :) – SomeShinyObject Mar 27 '13 at 10:55
  • onScreen is a OpenLayers API method for feature. These objects are OpenLayers feature objects. – Mo. Mar 27 '13 at 11:00
  • How expensive is it to push new objects in to an Array in JS? is it like java where re-sizing an array is very expensive? – Mo. Mar 27 '13 at 11:18
  • @Mo It depends on how big your object is but anything involving gigantic for loops and appending arrays with objects is going to be expensive. Check out my answer somewhere down there. It uses a little bit of recursion and a little bit of looping to transfer to the `dataArray` – SomeShinyObject Mar 27 '13 at 13:55

4 Answers4

0

Change the structure of the loop to :

var index = 0;
function myfunc() {
   while(features.length > index){
       var feature = features[v];
       //If the feature is on the map then we want to add it
       if(feature.onScreen()){     
          dataArray.push(feature.attributes);

       }
       index++;
       //You can do this
       var waitingTime = 0;
       If (index% 500=0) waitingTime=100; //or 10, 20...
       //Or just with a short interval
       var waitingTime = 10;
       setTimeOut('myfunc', waitingTime);
   }
}

Or with a parameter : [I prefer this one]

function myfunc(index) {
    if(!index) {
       index=0;
    }

    while(features.length > index){
       var feature = features[v];
       //If the feature is on the map then we want to add it
       if(feature.onScreen()){     
          dataArray.push(feature.attributes);
       }
        //You can do this
       var waitingTime = 0;
       If (index% 500=0) waitingTime=100; //or 10, 20...
       //Or just with a short interval
       var waitingTime = 10;
       setTimeOut('myfunc', waitingTime);
   }
}

[EDIT]
Change setTimeOut calls...

And in your code, when you call the function, don't give the param, to initialize the index!

JoDev
  • 6,633
  • 1
  • 22
  • 37
  • Hi JoDev, Does this mean that we will set a timeout every iteration? – Mo. Mar 27 '13 at 11:19
  • Yes setTimeOut allow you to iterate with a sleep time...the function will be call only once each time. – JoDev Mar 27 '13 at 12:45
  • Is it a good idea to set a timeout each time? or would it not be more efficient to do it every lets say 500 times? – Mo. Mar 27 '13 at 12:55
  • also if I am not mistaken, even though you called setTimeOut('myfunc', 500); at the end, the while loop will still continue and then after 500 milli seconds it will call the method again. – Mo. Mar 27 '13 at 13:02
  • Yes juste move the timeout code into the while... i will change it asap – JoDev Mar 27 '13 at 13:10
0

Not sure... but what about putting that code into a function, then breaking out and recalling the function (say every xMs)?

eg,

var LastPos = 0;

function DoLoop()
{


    for(var v = LastPos; features.length > v; v++){
                var feature = features[v];
                //If the feature is on the map then we want to add it
                if(feature.onScreen()){     
                    dataArray.push(feature.attributes);

                }

               if(v > 500)
               {
                   LastPos = v;
                   break;
               }
    }
    setTimeout('DoLoop()', 10);
}
John Mc Murray
  • 363
  • 5
  • 17
  • 1
    Passing `DoLoop()` as a string like that is a form of `eval` which is super slow. This would only help to slow down the process rather than optimize it. – SomeShinyObject Mar 27 '13 at 12:52
0

setTimeOut() can solve this.

var maxStatement = 1000, currentIndex=0, timeoutVar = ''
var sLimit = features.length

function multiStepLoop(){
    for (var i=currentIndex; i<currentIndex+maxStatement; i++){
        //---DO YOUR STUFFS HERE
    }

    var a = sLimit-i;
    currentIndex += maxStatement;
    if (maxStatement >= a){ maxStatement=a }
    if (a<=0){
        callBackFunction() //-- function to call when your peocess finish.
    }else{
        timeoutVar = setTimeout('multiStepLoop()',1)
    }
}
multiStepLoop();

The drawback is that you need to cover all you want to do after this process complete into a function, then run it like a callBack_Function.

Note : Jus need to set the time for setTimeout to 1 millisecond & the browser won't display the waiting-cursor.

vantrung -cuncon
  • 10,207
  • 5
  • 47
  • 62
0

What if you did something with a little burst and a little recursion:

Fiddle Code

Main Function

What happens is a given index is passed with a burst amount of intervals (i.e. 500), upon ending the for loop at the given number of burst iterations, it recalls itself with the same burst number. The function ends when the end of the features array is completed.

var transferFeatures = function (index, burst) {
    for (var z = 0; z <= burst; z++) {
        if (index === features.length) {
            return;
        }

        var feature = features[index];

        if (feature.onScreen) {
            dataArray.push(feature.attributes);
        }
        index++;
    }
    if (index !== features.length) {
        transferFeatures(index, burst);
    }
};

Testing

To simulate load, I created an array of objects using various key-value pairs (most importantly, your attributes inner object):

//To simulate a random boolean
var randomBoolean = function () {
    return Math.random() <= 0.5;
};

//A random integer
var getRand = function () {
    return (Math.floor(Math.random() * 10).toString());
};

// Create a bunch of dummy objects
var randomFeatures = function (arr, i) {
    for (var p = 0; p < i; p++) {
        arr.push({
            onScreen: randomBoolean(),
            attributes: {
                width: getRand(),
                height: getRand(),
                someAtt: "I'm just an attribute",
                coolKidsRideBikes: true,
                foo: "bar",
                bar: "baz"
            }
        });
    }
};

Granted, it's not the same onScreen() test you will be using, but either way, it evaluates to a boolean value. I think if you apply this concept with your code, you could have amazing results.

Everything in the Fiddle I linked too at the top is called like this:

randomFeatures(features, 5000);
console.log(features.length);
transferFeatures(0,500);
console.log(dataArray.length);

Load Testing

I simulated 5000000 (5 million) random objects being pushed onto features with a burst of 1000 and the script completed in around 3.29 seconds. Simulated load

SomeShinyObject
  • 7,581
  • 6
  • 39
  • 59