- We design and build extraordinary applications for companies looking to make the next great idea a reality.
- learn more
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?
Topics: Javascript
Leave a comment
About Pathfinder
Recent
- Bandwidth profiling Flex projects and more with Charles
- iPhone SDK: UIViewController Testing & TDD
- Icons are evil; so are menus - unless you do them right
- The Truth About Designing For Security
- GWT, Gadgets and OpenSocial, Part 2
- Has Many has_many: A Refactoring Story
- The Hidden Power of Canvas
- Review of fixture_replacement2 plugin
- Chess Game Viewer in GWT
- From JSP to Ruby on Rails: First thoughts on front-end coding conventions
Archives
- November 2008
- October 2008
- September 2008
- August 2008
- July 2008
- June 2008
- May 2008
- April 2008
- March 2008
- February 2008
- January 2008
- December 2007
- November 2007
- October 2007
- September 2007
- August 2007
- July 2007
- June 2007
- May 2007
- April 2007
- March 2007
- February 2007
- January 2007
- December 2006
- November 2006
- October 2006
- September 2006
- August 2006
- July 2006
- June 2006
- May 2006
- April 2006
- March 2006

