8

I have a fairly long running (3 to 10 second) function that loads data in the background for a fairly infrequently used part of the page. The question I have is what is the optimal running time per execution and delay time between to ensure that the rest of the page stays fairly interactive, but the loading of the data is not overly delayed by breaking it up?

For example:

var i = 0;
var chunkSize = 10;
var timeout = 1;
var data; //some array

var bigFunction = function() {
    var nextStop = chunkSize + i; //find next stop
    if (nextStop > data.length) { nextStop = data.length }

    for (; i < nextStop; i++) {
       doSomethingWithData(data[i]);
    }
    if (i == data.length) { setTimeout(finalizingFunction, timeout); }
    else { setTimeout(bigFunction , timeoutLengthInMs); }     
};

bigFunction(); //start it all off

Now, I've done some testing, and a chunkSize that yields about a 100ms execution time and a 1ms timeout seem to yield a pretty responsive UI, but some examples I've seen recommend much larger chunks (~300ms) and longer timeouts (20 to 100 ms). Am I missing some dangers in cutting my function into too many small chunks, or is trial and error a safe way to determine these numbers?

palswim
  • 11,856
  • 6
  • 53
  • 77
jball
  • 24,791
  • 9
  • 70
  • 92
  • 1
    You do realize that being JavaScript (client-side) CPU, memory, network connection, etc of the machine factors in greatly. – Jason McCreary Nov 10 '10 at 22:16
  • 1
    @Jason McCreary, of course. That's why I'm asking this question and not assuming that my trial and error method of determining these numbers is safe. – jball Nov 10 '10 at 22:20

2 Answers2

3

Any timeout value less than roughly 15ms is equivalent - the browser will update the DOM, etc and then execute the timeout. See this and this for more info. I tend to use setTimeout(fn, 0).

I would check the time elapsed instead of guessing numbers, because as Jason pointed out, there will be speed differences between clients:

var data; // some array
var i = 0;
var MAX_ITERS = 20; // in case the system time gets set backwards, etc
var CHECK_TIME_EVERY_N_ITERS = 3; // so that we don't check elapsed time excessively
var TIMEOUT_EVERY_X_MS = 300;

var bigFunction = function () {
    var maxI = i + MAX_ITERS;
    if (maxI > data.length) { maxI = data.length }

    var nextTimeI;
    var startTime = (new Date()).getTime(); // ms since 1970
    var msElapsed;

    while (i < maxI) {
        nextTimeI = i + CHECK_TIME_EVERY_N_ITERS;
        if (nextTimeI > data.length) { nextTimeI = data.length }

        for (; i < nextTimeI; i++) {
            doSomethingWithData(data[i]);
        }

        msElapsed = (new Date()).getTime() - startTime;
        if (msElapsed > TIMEOUT_EVERY_X_MS) {
            break;
        }
    }

    if (i == data.length) {
        setTimeout(finalizingFunction, 0);
    } else {
        setTimeout(bigFunction , 0);
    }
};

bigFunction(); //start it all off
Community
  • 1
  • 1
Alex
  • 589
  • 4
  • 7
  • I like the idea of timing it on the actual client, though I think I would just use the first run to get a rough average of time per iteration, and then set all the subsequent runs to a reasonable count based on that. – jball Nov 18 '10 at 06:10
0

The 1ms timeout is not actually 1 ms. By the time the thread yields, to achieve the 1ms, it probably yielded much more - because the typical thread time-slice is 30ms. Any number of other threads may have executed during the 1ms timeout, which could mean that 200ms went by before you received control again.

Why not just have the 'doSomethingWithData' execute in an entirely different thread?

Brent Arias
  • 29,277
  • 40
  • 133
  • 234
  • 1
    This is javascript: there's only one thread. At least, with browsers right now there's only one thread. – Cameron Skinner Nov 10 '10 at 22:25
  • Until web workers are commonplace (90+% of browsers), I'm not counting on having any more than one thread to execute my javascript in. – jball Nov 10 '10 at 22:26