JS中的网站加载结构

I have a web page that displays a chart with a lot of data in it. The chart is a 3rd party flash component.

A lot of the data is lazy loaded and fed into the chart in AJAX after initial page load. My issue is that while this happens, the page freezes several times until all data is loaded. This is a problem, especially because the page loads, and it seems like everything is ready, but then while data is being loaded with AJAX, the page freezes. A good example is a setInterval I have which increments a clock every 1 second. This clock visually freezes during the AJAX load procedure.

How can this be avoided? Would you recommend a different loading structure?

Your problem is actually pretty straightforward, though the solution is a little tricky. The reason you're experiencing the "freezes" is that JavaScript is single-threaded. The drawing of the page happens on the same thread as the processing of your data. So if you have one chunk of data that takes a long time to process, you'll experience user interface freezes.

Consider the following example:

http://jsfiddle.net/VT4Rs/

Note: You might need to spam the "STOP" button a bit if the screen is frozen when you click it. Also, you might need to adjust the "WEIGHT" variable to prevent super-long freezes if your computer/browser is less awesome than mine, or to cause the freezes to happen if your computer/browser is more awesome than mine.

Anyway, the point is that you'll notice the "Clock" will periodically freeze while it's looping through all those numbers. The reason for this is because the clock update is happening on the same thread as the data "processing." The way setInterval() works, it will add an event (to run the function it's passed) to your event queue every X milliseconds. HOWEVER, in order to prevent some serious problems from situations where your function takes more than X milliseconds to run, it will skip adding a new event to the event queue if a previous iteration of the event is already in the queue.

So if you have some random processing that is hogging your thread, then your setIntervals will apparently work improperly. Also, since the browser interface updates on the same thread, you can also cause "freezes."

The best way I've found to solve this is to break your data processing into more manageable "chunks" and then use setTimeout(fn, 0) to push those chunks onto the event queue periodically while allowing for some browser painting and other JavaScript to process as needed. So take a look at this updated example and see the difference. We're still "processing" the same data, but we're breaking it into smaller chunks:

http://jsfiddle.net/Sjk29/

The trick becomes deciding how big to make the chunks. You'll find that adjusting the chunks to be too large results in stalls and missed intervals. But if you make it too small, it takes forever to process the data because of the built-in overhead from each chunk.

The good news is that you're not restricted to a specified size. In my example, the chunk size is determined by a variable, and you might notice that the variable is referenced each chunk. This means you can adjust that variable to adjust the chunk size depending on the actual performance on the user's browser. You might do something like this:

var CHUNK_SCALE = 8;
var CHUNK_SIZE = function() { return Math.pow(10, CHUNK_SCALE); };

(function() {
    var lastTick = new Date();

    setInterval(function() {
        var now = new Date();
        if (now - lastTick > 1500) {
            CHUNK_SCALE--;
        }
        lastTick = now;
    }, 1000);
}());

(See: http://jsfiddle.net/ewP96/ )

This way, if the chunk size starts out too large (in a way that could impact UI performance), then it will automatically scale it back to something the user's computer can handle.

JavaScript is pretty awesome, eh?

I suggest you use html5 instead of flash for plotting, this may solve your freezing problem. You could look up:

  1. http://g.raphaeljs.com/
  2. http://www.rgraph.net/
  3. http://www.zingchart.com/
  4. http://d3js.org/
  5. http://processingjs.org/