fUrious
fUrious

Reputation: 444

Not truly async?

I have a array of about 18 000 elements. I'm creating a map application where I want to add the elements when the user zooms in to a certain level.

So when the user zooms in under 9 I loop tru the array looking for elements that is in the view.

However, it does take some time looping thru the elements, causing the map application lag each time the user zooms out and in of "level 9". Even if there are no elements to add or not, so the bottleneck is the looping I guess.

I've tried to solve it by asyncing it like:

          function SearchElements(elementArr) {

                var ret = new Promise(resolve => {

                var arr = [];

                for (var i in elementArr) {

                    var distanceFromCenter = getDistanceFromLatLonInKm(view.center.latitude, view.center.longitude, dynamicsEntities[i].pss_latitude, dynamicsEntities[i].pss_longitude);
                    var viewWidthInKm = getSceneWidthInKm(view, true);

                    if (distanceFromCenter > viewWidthInKm) continue;

                    arr.push(elementArr[i]);
                }

                resolve(arr);
            });

            return ret;
        }

        SearchElements(myElementsArray).Then(arr => {
            // ... 
        });

But its still not async, this method hangs while the for loop runs.

Upvotes: 2

Views: 71

Answers (1)

Jaromanda X
Jaromanda X

Reputation: 1

Because you still have a tight loop that loops through all the elements in one loop, you'll always have the responsiveness issues

One way to tackle the issue is to works on chunks of the data

Note: I'm assuming elementArr is a javascript Array

function SearchElements(elementArr) {
    var sliceLength = 100; // how many elements to work on at a time
    var totalLength = elementArr.length;
    var slices = ((totalLength + sliceLength - 1) / sliceLength) | 0; // integer
    return Array.from({length:slices})
    .reduce((promise, unused, outerIndex) => 
        promise.then(results => 
            Promise.resolve(elementArr.slice(outerIndex * sliceLength, sliceLength).map((item, innerIndex) => {
                const i = outerIndex * sliceLength + innerIndex;
                const distanceFromCenter = getDistanceFromLatLonInKm(view.center.latitude, view.center.longitude, dynamicsEntities[i].pss_latitude, dynamicsEntities[i].pss_longitude);
                const viewWidthInKm = getSceneWidthInKm(view, true);
                if (distanceFromCenter <= viewWidthInKm) {
                    return item; // this is like your `push`
                }
                // if distanceFromCenter > viewWidthInKm, return value will be `undefined`, filtered out later - this is like your `continue`
            })).then(batch => results.concat(batch)) // concatenate to results
        ), Promise.resolve([]))
    .then(results => results.filter(v => v !== undefined)); // filter out the "undefined"
}

use:

SearchElements(yourDataArray).then(results => {
    // all results available here
});

My other suggestion in the comment was Web Workers (I originally called it worker threads, not sure where I got that term from) - I'm not familiar enough with Web Workers to offer a solution, however https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers and https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API should get you going

To be honest, I think this sort of heavy task would be better suited to Web Workers

Upvotes: 2

Related Questions