Introducing Stratified JavaScript
This blog post is quite old now. Please see stratifiedjs.org for more up-to-date information about SJS
Here's a sneak preview of a project I've been working on in my spare time for a while, and which is nearing its first release:
'Stratified JavaScript' ('SJS') is a cross-browser extension to JavaScript which adds some concurrency features to the language. Without going into too much detail, the main features include:
(1) The ability to pause execution
The function
hold(time_ms);
pauses execution of a piece of code for time_ms milliseconds. E.g.:
var elem = document.getElementById("animated_element");
var x = 0;
while (true) {
elem.style.left = x;
x = (x + 10) % 200;
hold(100);
}
(full sample: animate.html)
Note that 'hold' doesn't busy wait. The browser's UI stays fully functionally while this code executes.
(2) Fork-Join Parallelism
In conventional JavaScript, arguments to function calls are evaluated sequentially. In Stratified JavaScript, they are evaluated concurrently:
function animate(elemname, duration_ms, step_ms) {
var elem = document.getElementById(elemname);
var start_time = new Date();
var x = 0;
do {
elem.style.left = x;
x = (x + 10) % 200;
hold(step_ms);
} while (duration_ms > new Date() - start_time);
}
function par(a, b) {
alert("all done");
}
par(animate("animated_element_1", 10000, 100),
animate("animated_element_2", 7000, 80));
(full sample: forkjoin.html)
Here, the two 'animate' calls are being executed concurrently. Once they both return, the body of 'par' gets called.
(3) Exploratory parallelism
Many concurrency problems fit into the pattern
"Explore options a,b,c,... concurrently. Once one of them yields a satisfactory result, return it and abort exploration of the remaining options."
Exploratory parallelism in Stratified JavaScript is embodied by the '@' ('alt', 'alternatives') operator:
animate("animated_element_1", 10000, 100) @
animate("animated_element_2", 7000, 80);
alert("all done");
(full sample: alt.html)
This code pops up 'all done' after 7s (after the animation of animated_element_2 has finished). At the same time, the animation of animated_element_1 will be aborted.
In many ways, exploratory parallelism forms the heart of Stratified JavaScript. It is applicable to many situations which are very cumbersome to express in conventional thread-based parallelism.
(4) Suspend/Resume
Most JavaScript programs live by asynchronous events, e.g. events generated by button clicks, or asynchronous XMLHttpRequests. Stratified JavaScript contains a generic mechanism for converting these asynchronous constructs into synchronous ones:
suspend {
... resume() ...
}
retract {
...
}
finally {
...
}
This will be executed by first evaluating the code in the 'suspend' block and then suspending execution. Execution will resume when the 'resume' function defined in the suspend block is called. The optional 'retract' block will be executed if the current code is aborted before 'resume' was called. The optional 'finally' block will be executed in either case; if execution was resumed, or if execution was aborted.
E.g., this is how an event listener could be 'synchronized':
function waitForEvent(event, elem) {
suspend {
var rv;
var listener_func = function(e) {
rv = e;
resume();
};
elem.addEventListener(event, listener_func, false);
}
finally {
elem.removeEventListener(event, listener_func, false);
}
return rv;
}
With this function we can now e.g. wait for a button click:
while (true) {
waitForEvent("click", document.getElementById('button1'));
alert("You clicked the button");
}
(full sample: suspend.html)
Or something more complicated:
function dump(message) {
document.getElementById("output").innerHTML = message;
}
while (true) {
var e = waitForEvent("click", document.getElementById('button1')) @
waitForEvent("click", document.getElementById('button2')) @
hold(5000);
if (e)
dump("You clicked button '"+e.target.id+"'");
else
dump("Click a button already!");
}
(full sample: suspend2.html)
If all of this sounds similar to Oni, that's because it is. Under the hood, Stratified JavaScript is executed by a runtime based on Oni. Everything that can be expressed in Stratified JavaScript can be written directly in Oni without going through any compilation stage. The advantage of Stratified JavaScript is that it doesn't have the awkward functional syntax of Oni.