It looks like people are now writing compilers to deal with node js spaghetti callback code. These are available for C#, Javascript (useable with nodejs) and Scala.
C# 5.0
C# 5.0 will have await/async keywords
http://msmvps.com/blogs/jon_skeet/archive/2011/05/08/eduasync-part-1-introduction.aspx
you can write non-blocking http handler like:
public async void get(Request request, Response response) {
var json = await Redis.get(request.getParam("id"));
await response.write(json);
}
Redis#get and Reponse#write are both async methods
if you need to do parallel handling:
public async void get(Request request, Response response) {
var json1Task = Redis.get(request.getParam("id"));
var json2Task = Redis.get(request.getParam("id"));
await response.write(await json1Task + await json2Task);
}
Scala
Scala has continuations using the shift/reset keywords which seems to be much harder to grock than await/async.
http://www.scala-lang.org/node/2096
However, if you wrote a proper non-blocking library you would probably never use shift/reset in client code. it would all be in the library. if you have a shift which is not nested in a reset then the method becomes CPS and if you have a method that calls a CPS method it becomes CPS as well. it spreads a bit like a virus infecting everything it touches. so it is mostly transparent.
you can write non-blocking http handler like:
def get (request: Request, response: Response) = {
val json = Redis.get(request.getParam("id"))
response.write(json)
}
#get, Redis#get and Reponse#write are all CPS.
if you need to do parallel handling then:
def get (request: Request, response: Response) = {
val json1Future = Redis.getAsync(request.getParam("id1"))
val json2Future = Redis.getAsync(request.getParam("id2"))
response.write(json1Future.await, json2Future.await)
}
here Future#await, #get and Response#write are CPS. Redis#getAsync is a normal method that returns a Future that has a CPS await method.
the main difference between c# and scala is in c# the caller is able to choose whether they want to (appear to) block on an async method while in scala the callee has to expose a blocking and a non-blocking version of the method. It appears to be possible to create a function that convert a CPS blocking method to a method that returns a Future. I have some code that does it for 1/arity methods. https://gist.github.com/1191571 I suspect that there is a much simpler way of doing it but I don’t understand scala continuations enough to work it out
Javascript
For javascript there are many options
Streamline JS (https://github.com/Sage/streamlinejs) has been written specifically for nodejs.
You can write a non-blocking http handler like so:
function get(request, response, _) {
var json = Redis.get(request.getParam("id"), _);
response.write(json, _);
}
which compiles to:
function get(request, response, _) {
var json;
var __frame = {
name: "get",
line: 1
};
return __func(_, this, arguments, get, 2, __frame, function __$get() {
return Redis.get(request.getParam("id"), __cb(_, __frame, 1, 13, function ___(__0, __1) {
json = __1;
return response.write(json, __cb(_, __frame, 2, 2, _));
}));
});
};
There is also TameJS which doesn’t have magical error handling:
https://github.com/maxtaco/tamejs
You can write a non-blocking http handler like so:
function get(request, response) {
await {Redis.get(request.getParam("id"), defer(var err, var json))};
// error handling cut out
await {response.write(json, defer(var err))};
// error handling cut out
}
There is also:
Narrative JS (http://www.neilmix.com/narrativejs/doc/example.html)
Stratify JS (http://onilabs.com/stratifiedjs)
and there was an attempt to get a defer keyword added to coffee script
https://github.com/jashkenas/coffee-script/issues/350