Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to iterate over an array without blocking the UI

I am needing to iterate over some large arrays and store them in backbone collections from an API call. What is the best way to do this without making the loop cause the interface to become unresponsive?

The return of the ajax request also blocks as the data returned is so large. I figure that I could split it up and use setTimeout to make it run asynchronously in smaller chunks but is there an easier way to do this.

I thought a web worker would be good but it needs to alter some data structures saved on the UI thread. I have tried using this to do the ajax call but when it returns the data to the UI thread there is still a time when the interface is unresponsive.

Thanks in advance

like image 728
georgephillips Avatar asked Apr 27 '12 03:04

georgephillips


People also ask

What is the most common way to iterate through an array?

Iterating over an array You can iterate over an array using for loop or forEach loop. Using the for loop − Instead on printing element by element, you can iterate the index using for loop starting from 0 to length of the array (ArrayName. length) and access elements at each index.

What is the simplest way to iterate through the items of an array?

Iterating over an array means accessing each element of array one by one. There may be many ways of iterating over an array in Java, below are some simple ways. Method 1: Using for loop: This is the simplest of all where we just have to use a for loop where a counter variable accesses each element one by one.

How do you iterate through an array in HTML?

The forEach() method can now be used to iterate over the elements like an array and display them. The elements can be iterated through by using a normal for loop. The number of elements in the HTMLCollection can be found out by using the length property of the collection.

Which method is used to iterate over arrays and objects?

. map() can be used to iterate through objects in an array and, in a similar fashion to traditional arrays, modify the content of each individual object and return a new array.


1 Answers

You have a choice of with or without webWorkers:

Without WebWorkers

For code that needs to interact with the DOM or with lots of other state in your app, you can't use a webWorker so the usual solution is to break your work into chunks do each chunk of work on a timer. The break between chunks with the timer allows the browser engine to process other events that are going on and will not only allow user input to get processed, but also allow the screen to draw.

Usually, you can afford to process more than one on each timer which is both more efficient and faster than only doing one per timer. This code gives the UI thread a chance to process any pending UI events between each chunk which will keep the UI active.

function processLargeArray(array) {     // set this to whatever number of items you can process at once     var chunk = 100;     var index = 0;     function doChunk() {         var cnt = chunk;         while (cnt-- && index < array.length) {             // process array[index] here             ++index;         }         if (index < array.length) {             // set Timeout for async iteration             setTimeout(doChunk, 1);         }     }         doChunk();     }  processLargeArray(veryLargeArray); 

Here's a working example of the concept - not this same function, but a different long running process that uses the same setTimeout() idea to test out a probability scenario with a lot of iterations: http://jsfiddle.net/jfriend00/9hCVq/


You can make the above into a more generic version that calls a callback function like .forEach() does like this:

// last two args are optional function processLargeArrayAsync(array, fn, chunk, context) {     context = context || window;     chunk = chunk || 100;     var index = 0;     function doChunk() {         var cnt = chunk;         while (cnt-- && index < array.length) {             // callback called with args (value, index, array)             fn.call(context, array[index], index, array);             ++index;         }         if (index < array.length) {             // set Timeout for async iteration             setTimeout(doChunk, 1);         }     }         doChunk();     }  processLargeArrayAsync(veryLargeArray, myCallback, 100); 

Rather than guessing how many to chunk at once, it's also possible to let elapsed time be the guide for each chunk and to let it process as many as it can in a given time interval. This somewhat automatically guarantees browser responsiveness regardless of how CPU intensive the iteration is. So, rather than passing in a chunk size, you can pass in a millisecond value (or just use an intelligent default):

// last two args are optional function processLargeArrayAsync(array, fn, maxTimePerChunk, context) {     context = context || window;     maxTimePerChunk = maxTimePerChunk || 200;     var index = 0;      function now() {         return new Date().getTime();     }      function doChunk() {         var startTime = now();         while (index < array.length && (now() - startTime) <= maxTimePerChunk) {             // callback called with args (value, index, array)             fn.call(context, array[index], index, array);             ++index;         }         if (index < array.length) {             // set Timeout for async iteration             setTimeout(doChunk, 1);         }     }         doChunk();     }  processLargeArrayAsync(veryLargeArray, myCallback); 

With WebWorkers

If the code in your loop does not need to access the DOM, then it is possible to put all the time consuming code into a webWorker. The webWorker will run independently from the main browser Javascript and then when its done, it can communicate back any results with a postMessage.

A webWorker requires separating out all the code that will run in the webWorker into a separate script file, but it can run to completion without any worry about blocking the processing of other events in the browser and without the worry about the "unresponsive script" prompt that may come up when doing a long running process on the main thread and without blocking event processing in the UI.

like image 56
jfriend00 Avatar answered Oct 02 '22 13:10

jfriend00