Agile Ajax

Narrative Javascript - Cleaner Code for Long Running Tasks

All the major desktop operating systems use preemptive multitasking. Linux, Windows, and Mac OS have offered preemptive multitasking for so long that we barely remember any alternative. But in the olden days, back before all microprocessors had a supervisor mode, cooperative multitasking was often used to allow more than one program to run on the computer at one time.

What are these different forms of multitasking? To be brief, in preemptive multitasking the operating system interrupts the running program, saves its state, and switches control over to another program for a short amount of time -- a slice -- when that program too is interrupted and swapped out for another. Replace "program" with "thread" and you've jumped forward 10 years. In cooperative multitasking, each program explicitly gives up control to the operating system so that another program can run. This makes operating system design much less complicated, but it also depends on good programmer behavior to ensure that their program really does give up control and doesn't hog the system resources for itself.

Typically in these environments, programming languages provide support for cooperative multitasking at the language, library or system call level.

As Ajax applications drive the need for more sophisticated JavaScript functionality in the browser, the need for some form of multitasking will become more acute. There are some very simple ways of doing cooperative multitasking in the browser without triggering the dreaded "runaway script" dialog. We've all seen the following before:

var count = 100;// expensive functionfunction updateDOM() {    var elem = document.getElementById("txt");    elem.innerHTML = count;    count--;    return !(count > 0);}// scheduled funcfunction scheduled() {    var done = updateDOM();    if (!done) {        setTimeout(scheduled, 1000);    }}

Basically we're breaking up a loop into a hundred separate function calls that we schedule a minute apart. We keep the state between calls in our count variable. The above isn't too exciting and is sufficient for doing things like periodically updating DOM elements or making XHR requests of a server. But try to do anything more complicated than a simple loop, and the number of state variables and callback contortions soon gets out of hand.

An interesting development that has already been discussed elsewhere is Narrative JavaScript. It consists of a JavaScript preprocessor that turns a dialect of JavaScript that supports asynchronous blocking into vanilla JavaScript. When combined with the runtime library, you can do things as simple as implementing a sleep function. The compiler takes this (where the "->" indicates an asynchronous call):

function sleep(millis) {    var notifier = NjsRuntime.createNotifier();    setTimeout(notifier, millis);    notifier.wait->();}

And and turns it into this:

function sleep(millis){var njf1 = njen(this,arguments,"millis");nj:while(1) {try{switch(njf1.cp) { case 0:njf1._notifier=NjsRuntime.createNotifier();setTimeout(njf1._notifier,njf1._millis);njf1.cp = 1;njf1._notifier.wait(njf1);return;case 1:break nj; }} catch(ex) { if(!njf1.except(ex,1)) return; }} njf1.pf();}

The good news is you only have to maintain the former not the latter. Those asynchronous calls essentially enable you to perform cooperative multitasking without having to unroll your algorithms and implement them using callbacks.

Neil Mix, the author of Narrative JavaScript, has recently written up a post that describes some of the inner workings of his approach. From the post:

The narrativejs compiler works as follows: it finds function declarations that use the -> operator (i.e. contain yielding method calls) within the body, and then it rewrites these functions by doing the following:

  • reorganize the function body to act like a state machine
  • bundle all local context data into a referenceable object
  • modify all local data references to operate on the context object
  • modify yielding method calls so that the context is appended to the callee's parameter list

At runtime, the contexts that are passed into successive yielding method calls are linked together, thus forming a runtime representation of the current stack. It's this context data that's used to resume execution (at the point just after execution yielded) upon asynchronous callback.

There is a development and JavaScript 1.7 that may make all this unnecessary: generators and iterators. The following code example demonstrates a simple use:

function fib() {  var i = 0, j = 1;  while (true) {    yield i;    var t = i;    i = j;    j += t;  }}

var g = fib();for (var i = 0; i < 10; i++) {  document.write(g.next() + "<br>\n");}

The yield statement binds the value of i and returns it as well as control until the next time the next() method is called. You'll have to wait until Firefox 2 comes out to play with it, though. Still, even with this language feature there are still limits to what sort of cooperative multitasking can be done. Recursion anyone?

 
  Technorati : , , ,

Topics:

Leave a comment

Powered by WP Hashcash

About Pathfinder

  • We design and build extraordinary applications for companies looking to make the next great idea a reality.
  • learn more

Topics

WordPress

Comments about this site: info@pathf.com