score:2

Accepted answer

instead of trying to create threads and artificial breaks you could speed up your code by precalculation an image data array. you code so far has two flaws:

  1. you draw every single pixel individualy
  2. you redraw pixels that are already there

because of the sheer number of data points, those two flaws can add up really quickly. when you run your original fiddle you see that after just a second, the picture doesn't change anymore but you are still drawing pixels over the same area for maybe 20 further seconds. so here is an example of how can do it with the putimagedata() function:

function createpicturearray() {
    var res, i, max_i;
    var canvasel = d3.select('#test').node();
    var ctx = canvasel.getcontext('2d');
    var data;

    res = ctx.getimagedata(0, 0, width, height);
    data = res.data;
    for (i = 0, max_i = data.length; i < max_i; i++) {
        data[i] = 255;
    }
    return res;
}

function generatedata(size) {
    var i, x, y, s, res, data, randomx, randomy;

    randomx = d3.random.normal(width/2 , 80);
    randomy = d3.random.normal(height/2, 80);    

    res = createpicturearray();
    data = res.data;
    for (i = 0; i < size; i++) {

        x = parseint(randomx());
        y = parseint(randomy());
        s = 4 * y * width + 4 * x;
        if (data.length > s && data[s + 1] !== 0) {
            data[s + 1] = 0;
            data[s + 2] = 0;
        }
    }
    return res;
}

function main() {
    var canvasel = d3.select('#test').node();
    var ctx = canvasel.getcontext('2d');
    width = canvasel.width;
    height = canvasel.height;
    data = generatedata(2000000);
    ctx.putimagedata(data, 0, 0);

}
main()

fiddle

this example creates 2 million data points but feel free to increase it to 20 million. on my machine it took about 7 seconds. so there is still room for optimization but at least i didn't experienced severe freezing.

what happens is that you create your random numbers one after the other and for each number pair you calculate the correct pixel inside the image data array and you set that pixel to red if it has not already been set red.

score:1

both javascript and ui updates such as redraws and reflows run in a single "browser ui thread". the browser ui thread works on a queuing system where tasks are kept until the process is idle. once idle, the next task in the queue is retrieved and executed.

techniques like settimeout() and requestanimationframe defer execution of your task and allow some breathing space for the ui to update, but when those tasks are eventually run, it's still one at a time, not concurrently. thus any such task will always block the execution of any other.

html5 introduces web workers, which allow you to pass javascript to a separate thread, where it is executed concurrently with the ui thread. the javascript you execute within a web worker is isolated from the ui thread, and you cannot make any updates to the ui from the web worker. however a web worker would be ideal for something like your generate_data() function.

here's a great web worker tutorial and here's a browser compatibility matrix.

score:3

javascript is a single threaded system. functions that essentially create a callback like settimeout will still kill a single threaded system.

edit:

as per the comments, there are some creative ways to give the single thread javascript the illusion of executing large logic functions asynchronously, thus freeing up the process for executing things like click handlers.

@v3ss0n depending on where in your code the process freeze occurs you have a couple of options as follows:

1) process freezes during creation of the array: loading 20 mil items into an array could take a while, and i would recommend not doing this. there is more than 1 way to approach this, but i would move as much heavy looping logic as i can outside of javascript and possibly to a web-service. this has it's own challenges such as attempting to send 20 mil items back to the client, that is a bad idea. but if you can split it up into manageable groups of coordinates (20-100 max) and then make multiple web service calls as needed (using something like jquery ajax method http://api.jquery.com/jquery.ajax/) then all that your javascript has to worry about is appending the items. that will stream data to you over a period of time, so don't expect it to be instant, but should not freeze the browser if it is having difficulties looping through the coordinate creation function 20 mil time.

2) process freezes during rendering of each individual point: the fix for this is a little more creative and can also be used for the above process if you do not want to use web services. first look at the following code:

    var renderingcounter = 0;
var asynctimer = null; // keep the render timer global to prevent duplicate renders from being run

function startrender(){
    // test if existing async render is running
    if(asynctimer != null){
        // existing async timer, clear the timer and clear the canvas
        clearinterval(asynctimer);
        renderingcounter = 0;

        // code for clearing the canvas ..
    };

    // create interval object
    var asynctimer = setinterval(function (){
        // at the begining of each start render, test if all points have been rendered
        if(renderingcounter >= pointarray.length){
            // kill the asynctimer
            clearinterval(asynctimer);
            asynctimer = null;
            renderingcounter = 0; // reset the counter
            return;
        };

        while(renderingcounter < 100){ // only draw 100 items
            if(renderingcounter >= pointarray.length){ // make sure you are not out of array bounds
                return;
            };

            // execute display code for that point
        }; // loop
    }, 40);
}

the above code splits up the rendering process into 100 point groups. upon completion of a group it pauses for 40 milliseconds, this should free up enough time on the main thread to allow execution for other items to continues (such as ui). the global variable renderingcounter will keep the current progress through the array. the code also has checks for current running rendering timers if the function gets re-called during rendering (in the example it kills the current rendering and resets it). if the rendering timer still causes hangs, you can increase the pause interval.

those two methods of approaching asynchronous execution should provide enough flexibility to allow rendering or data reading from server side web services (should you ever go that direction) while still keeping a fluid ui


Related Query

More Query from same tag