README
slasp.js: SugarLess Asynchronous Structured Programming
This package is the result of a personal experiment. From that experiment, I concluded that making a JavaScript software application asynchronous (i.e. non-blocking) requires someone to "forget" about commonly used synchronous programming language constructs and replace them by different ones supporting asynchronous programming.
This package implements a module that provides artificially created asynchronous equivalents to common synchronous JavaScript programming language constructs. Most of these implement structured programming concepts.
There are many software abstractions available to make asynchronous programming more convenient and to cope with the so-called "callback hell". This library is different compared to others, because it sticks to the bare bones of the JavaScript language.
Unlike other software abstractions, it does NOT provide any sugar, such as asynchronous variants of utility functions found in functional programming languages, encapsulation of activities into objects, and so on.
However, it does provide more features when it comes to expressing things in an asynchronous world, such as asynchronous if/while/do-while test conditions and asynchronous object constructors and methods.
Sugar (of course) can still be built on top of this library.
Concepts
The JavaScript language implements a number of programming language concepts. The following table lists these concepts, how they are used while programming synchronous applications, and what the alternative should be used to make the same concept asynchronous:
Concept | Synchronous | Asynchronous |
---|---|---|
Function interface | function f(a) { ... } |
function f(a, callback) { ... } |
Return statement | return val; |
callback(null, val); |
Sequence | a; b; |
slasp.sequence([function (cb) { a; cb(); }, function(b) { b; cb(); } ]); |
if-then-else | if c() t() else e(); |
slasp.when(c, t, e); |
switch | switch(c()) { case "a": f(); break; } |
slasp.circuit(c, function(r, cb) { switch(r) { ... } }); |
Recursion | function fun() { fun(); } |
function fun(cb) { setImmediate(function() { fun(cb); }); |
while | while(c()) { s(); } |
slasp.whilst(c, s); |
doWhile | do { s(); } while(c()); |
slasp.doWhilst(s, c); |
for | for(s(); c(); inc()) { stmt(); } |
slasp.from(s, c, inc, stmt); |
for-in | for(var a in arr()) { stmt(); } |
slasp.fromEach(arr, function(a, callback) { stmt(callback); }); |
throw | throw err; |
callback(err); |
try-catch-finally | try { a(); } catch(err) { e(); } finally { f(); } |
slasp.attempt(a, function(err, callback) { funErr(cb); }, f); |
constructor | function Cons(a) { this.a = a; } |
function Cons(self, a, callback) { self.a = a; callback(null); } |
new | new Cons(a); |
slasp.novel(Cons, a, callback); |
For most of the concepts listed above, a function abstraction is needed to make an application asynchronous. These abstractions are provided by this library.
Usage
Currently, this library is tested for usage with Node.js and web browsers.
Node.js
Usage on Node.js is straight forward. It can be installed into a working directory with the NPM package manager by running:
$ npm install slasp
In the code, the module can be imported with:
var slasp = require('slasp');
Browser
For usage in the browser copy lib/slasp.js
into a folder accessible by a web
page. Then add the following script include to the HTML code of that web page:
<script type="text/javascript" src="slasp.js"></script>
An example
Consider the following synchronous JavaScript code fragment that uses an implementation of the Gregory–Leibniz formula to approximate pi up to 5 decimal places:
function printOnConsole(value) {
console.log(value);
}
function checkTreshold() {
return (approx.toString().substring(0, 7) != "3.14159");
}
var approx = 0;
var denominator = 1;
var sign = 1;
while(checkTreshold()) {
approx += 4 * sign / denominator;
printOnConsole("Current approximation is: "+approx);
denominator += 2;
sign *= -1;
}
Although the code above seem to do its job, it also takes a bit of time to complete. In this time window the environment such as a web browser or web server is blocked, because the event loop cannot process events.
To resolve the blocking issue, JavaScript's while
construct can be replaced by
a slasp.whilst
function invocation. Moreover, we can also make the expressions
and statements asynchronous by generating tick events:
var slasp = require('slasp');
function printOnConsole(value, callback) {
process.nextTick(function() {
console.log(value);
});
}
function checkTreshold(callback) {
process.nextTick(function() {
callback(null, approx.toString().substring(0, 7) != "3.14159");
});
}
var approx = 0;
var denominator = 1;
var sign = 1;
slasp.whilst(checkTreshold, function(callback) {
slasp.sequence([
function(callback) {
approx += 4 * sign / denominator;
callback(null);
},
function(callback) {
printOnConsole("Current approximation is: "+approx, callback);
},
function(callback) {
denominator += 2;
callback(null);
},
function(callback) {
sign *= -1;
callback(null);
}
], callback);
});
The above expression does not block an environment's event loop allowing a Node.js HTTP server to still handle incoming connections and a browser to still handle user input.
Overview
The slasp
module implements the following function abstractions:
sequence(stmts, callback)
Runs a number of asynchronous statements sequentially.
when(conditionFun, thenFun, elseFun, callback)
Runs a selection statement that executes a then statment when the conditional function yields true and an else statement when conditional statement yields false.
This function is an asynchronous variant of JavaScript's if-then-else
construct.
circuit(conditionFun, caseFun, callback)
Runs a statment in which a switch statement can be embedded so that a selection of multiple options can be done.
This function is supposed to represent an asynchronous variant of
JavaScript's switch
construct
whilst(conditionFun, statementFun, callback)
Keeps repeating a statement as long as the condition function yields true. The condition check is done at the beginning of each iteration.
This function represents an asynchronous variant of JavaScript's while
construct.
doWhilst(statementFun, conditionFun, callback)
Keeps repeating a statement as long as the condition function yields true. The condition check is done at the end of each iteration.
This function represents an asynchronous variant of JavaScript's do-while
construct.
from(startFun, conditionFun, stepFun, statementFun, callback)
Performs a repetition a given number of times.
This function represents an asynchronous variant of JavaScript's for
construct.
fromEach(startFun, statementFun, callback)
Iterates over the keys of an array or object.
This function represents an asynchronous variant of JavaScript's for-in
construct.
attempt(statementFun, captureFun, lastlyFun)
Attempts to execute a statement and executes a capture function if some exception has been thrown. Finally, it executes a lastly statement regardless of the outcome.
This function represents an asynchronous variant of JavaScript's
try-catch-finally
construct.
novel(constructorFun, ..., callback)
Constructs a new object from a given constructor function.
Constructor functions have almost the same signature as conventional synchronous ones. The main difference is that they use self instead of this to refer to the object that is constructed. The last parameter should be a callback providing the constructed object as a result, or an error if some exception has been thrown.
This function represents an asynchronous variant of JavaScript's new
operator.
Examples
The test suite that resides in the tests/
folder also serves an example
showcase:
In the
nodejs
folder testcases can be found showing synchronous JavaScript programs and their asynchronous counterparts using the function abstractions described earlierThe
html
folder contains a simple JavaScript program embedded in a web page that is not supposed to block the browser.
API documentation
This package includes API documentation, which can be generated with
JSDuck. The Makefile in this package
contains a duck
target to generate it and produces the HTML files in build/
:
$ make duck
License
The contents of this package is available under the MIT license