Stratified JavaScript and Multitasking
In response to my earlier blog post on Stratified JavaScript, Jonathan Cook asked an interesting question:
"[...] I am very interested in using SJS to eliminate the blocking nature of heavy computation in nested loops. A hold(0) would seem to do this, or perhaps a different method for this purpose would be appropriate?"
The short answer is, you don't need to call hold() or do anything special at all. Just code your computation as you would in 'normal' JavaScript, and SJS will take care of the rest; the UI will automatically stay responsive while your computation is chugging along.
Now for the slightly longer answer.
The SJS runtime contains a scheduler which preemptively schedules the different strands of execution ('strata') of an SJS program. This scheduler automatically yields control to the browser every so often, and it ensures that individual strata get a fair timeslice.
In other words, SJS implements a form of preemptive multitasking, rather than cooperative multitasking. There is no need to manually yield control like you would in e.g. Narrative JavaScript/Strands; SJS automatically does it in the background.
Here's an example of a heavy computation in SJS; a program to calculate a
few hundred digits of Pi:
pi.html
There are a couple of noteworthy things about this program:
Firstly, if you look at the source, there is no hold() or any other special SJS primitive. It is just 'straight' JS code, which is being preprocessed through the SJS compiler by virtue of sitting in a script tag with type "text/sjs".
Secondly, it runs painfully slow. This is a consequence of the fact that the SJS compiler doesn't perform any optimizations at all at this point; every JavaScript function call or operator application is converted into a potential yielding point, adding huge overhead to programs which are heavy on mathematical calculations.
This situation will improve dramatically when we get around to making the compiler smarter. A similar optimization in Oni resulted in a 2 orders of magnitude improvement!
In the meanwhile there is a workaround though: SJS code can freely
call 'normal' JavaScript code, so we can just move critical inner
loops into 'normal' JavaScript, as in this modified program:
pi-faster.html
You'll notice that this program runs a lot faster, while not really adding much complexity - just one inner loop has been moved to 'straight' JavaScript.
Just to prove that the preemptive scheduler works as advertised, here are another couple of examples which add some SJS-specific features.
Firstly, this program adds an animation running in parallel to the Pi calculation:
pi-and-animation.html
Secondly, and finally, this program adds a 'UI' for turning the animation on and off:
pi-and-animation-and-events.html
Take a look at the animation loop:
function animateLoop() {
var elem = document.getElementById("toggle_animation");
while (true) {
waitForEvent("click", elem);
elem.innerHTML = "Stop animation";
animate() @ waitForEvent("click", elem);
elem.innerHTML = "Restart animation";
}
}
Isn't it beautiful? :-)