Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How much to subdivide long running function for responsive UI?

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?

like image 483
jball Avatar asked Nov 10 '10 22:11

jball


1 Answers

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
like image 68
Alex Avatar answered Sep 19 '22 17:09

Alex