Unauthorized reproduction is prohibited
1. Processing of expensive calculations
Perhaps the most complex aspect of developing complex Javascript applications is the single-threaded nature of the user interface. The best situation when Javascript handles user interaction is slow response, and the worst situation is unresponsiveness causing the browser to hang (when Javascript is executed, all update operations in the page are suspended). Due to this fact, it is imperative to reduce all complex operations (any calculation longer than 100ms) to a manageable level. In addition, if the script does not stop after running for at least 5 seconds, some browsers (such as Firefox and Opera) will generate a prompt box to warn the user that the script does not respond.
This is obviously undesirable, and producing an unresponsive interface is not good. However, this is almost certainly the case when you need to process large amounts of data (such as processing thousands of DOM elements).
At this time, the timer is particularly useful. Because the timer can effectively pause the execution of Javascript code, it can also prevent the browser from hanging the executing code (as long as the individual code is not enough to cause the browser to hang). With this in mind, we can incorporate normal, intensive, loop calculations into non-blocking calculations. Let's look at the following example where this type of calculation is required.
A long-running task:
<table><tbody></tbody></table>
// Normal, intensive, operation
var table = document.getElementsByTagName("tbody");
for ( var i = 0; i < 2000; i++ ) {
var tr = document.createElement("tr");
for ( var t = 0; t < 6; t++ ){
var td = document.createElement("td");
td.appendChild(document.createTextNode("" + t));
tr.appendChild(td);
}
table.appendChild(tr);
}
}
In this example, we create a total of 26,000 DOM nodes and populate the numbers into a table. This is too expensive and will most likely hang the browser and prevent normal user interaction. We can introduce timers into this and get different, perhaps better results.
Use timers to break up long-running tasks:
<table><tbody></tbody></table>
var table = document.getElementsByTagName("tbody");
var i = 0, max = 1999;
setTimeout(function(){
for ( var step = i + 500; i < step; i++ ) {
var tr = document.createElement("tr");
for ( var t = 0; t < 6; t++ ){
var td = document.createElement("td");
td.appendChild(document.createTextNode("" + t));
tr.appendChild(td);
}
}
table.appendChild(tr);
}
if ( i < max )
setTimeout( arguments.callee, 0 );
}, 0);
In our modified example, we split the intensive computation into four parts, each creating 6500 nodes. These calculations are unlikely to interrupt the browser's normal flow. The worst case scenario is that these numbers may be adjusted at any time (for example, make them vary between 250-500, so that each of our cells will generate 3500 DOM nodes). But what's most impressive is how we changed our code to adapt to the new asynchronous system. We need to do more work to ensure that the numbers for the elements are generated correctly (the loop doesn't end forever). The code is very similar to our original one. Note that we use closures to maintain the iteration state between code fragments. Without closures, this code will undoubtedly be more complex.
There is a clear change using this technology compared to the original. Long browser hangs are replaced by 4 visual page updates. Although the browser tries to execute these code snippets as quickly as possible, it also renders DOM changes (like massive updates) after each step of the timer. In most cases, users are unaware of these types of updates, but it's important to remember that they happen.
There is one situation where this technology would work specifically for me - an application I built to calculate the schedules of college students. At first, the application was typical CGI (the client talked to the server, the server calculated the schedule and returned it). But I changed it and put all schedule calculations on the client. This is the view of schedule calculations:
These calculations are quite expensive (thousands of permutations need to be traversed to find the correct answer). This problem is solved by splitting the schedule calculation into actual units (updating the user interface with the completed part). In the end, users delivered a user interface that was fast, responsive, and usable.
It’s often surprising how useful technology can be. You'll find it often used in long-running programs, like test boxes (we discuss this at the end of this chapter). More importantly, this technology shows us how easy it is to work around the limitations of the browser environment while also providing users with a rich experience.