JavaScript and Screen Responsiveness
Posted on 2010-06-01 23:38 in Blog
Lately, I’ve been playing around with JavaScript, creating a visualizer for my next AI game competition (in the fall?). I’ve decided to deviate from my past setup; instead of displaying live games on the over head, I will instead be recording player action to a log file and then replaying the results on the overhead. This log file can then be provided to the participant, allowing them a detailed look at all the steps their AI took during a sample game.
The problem I discovered involved the rate at which changes are painted to the screen.
$(".note").html("Message one");
sleep(5000); // example 5 second sleep routine
$(".note").html("Message two");
With the above code snippet, one would expect Message one to appear, be displayed for five seconds, and then get replaced with Message two. Instead, something surprising happens. When the script is run, no message appears, the page freezes for five seconds, and then the second message appears. The first message is never displayed.
The cause for this is quite surprising. It appears most browsers (the three I tested) wait until the JavaScript function has completed running before it starts updating the displayed page. I believe this was the case initially because the JavaScript interpreter and the page rendered ran in the same thread, so the two tasks could not be accomplished concurrently. Today, this behavior is probably maintained the same for backwards compatibility. I cannot fathom why this behavior would be written into a JavaScript specification, so that is probably not the culprit.
The fix appears simple, but can get quite complicated. JavaScript provides a time routine, that runs a function of your choosing during each timer tick. (JavaScript does allow for multiple timers to run in parallel)
var id = setInterval ( "desiredAction()",1000 );
function desiredAction ( ){
// action code
if(lastAction){
clearInterval( id );
}
}
This is great if you want to perform the same operation for each tick, but in my case, sometimes I wanted to update text, other times move images. To solve this problem, as I parsed the log file, I queued up all the different events in an array. During the timer tick, I removed the first item in the queue and called the associated function pointer to perform the desired specific action.
function performEventQueueAction(){
// verify there are actions left
if(window.game.eventQueue.length > 0){
var action = window.game.eventQueue.shift(); // remove item in front of array
action.func( action.data ); // use function pointer with saved data
}
// stop rendering when actions complete
else{
clearInterval( window.game.eventInterval ); // stop timer
}
}
With this tasking, the page stays responsive because the event handler is available to respond to mouse clicks for browsing to the different portions of the page, and all the game display events occur in the order desired, in the approximate time desired.