UPDATE: My hosting provider has been very flaky due to a lot of inbound trafic from reddit.com. Please use the mirror in the mean time, until things stabilize a bit. Everything has recovered now. You can also check out the new Er.js homepage.
I’ve just posted up a fun hack that I’ve been playing with the last few days… it’s called Erjs; short for Erlang-in-JavaScript.
Erjs piggybacks on Neil Mix’s Thread.js which fakes threading in JavaScript 1.7 using coroutines and nested generator continuations. My idea was to replicate Erlang’s concurrent lockless process model and message-passing in JavaScript.
Running a JavaScript function in the background is easy with Erjs:
Er.spawn(myBackgroundFunction);
Er.spawn starts a new Erjs process running myBackgroundFunction, and returns its process id.
Because processes are really coroutines, you have to call yield before any function which might block. Calling yield will create a continuation trampoline that can be rerun by Erjs when it’s time for the process to continue executing. For example:
function myBackgroundFunction() {
// Wait for 4 seconds
yield Er.sleep(4000);
// Do other things...
}
In Erlang and Erjs, each process has a built-in message queue that other processes use to send it messages. Posting to the message queue never blocks the caller, and the destination process for the message can read them off the queue whenever it wants. Messages are just regular associative arrays, similar to hashtables, which are easy to create in JavaScript. For example:
Er.send(myPid, { Hello: new Date(), From: Er.pid() });
Here, myPid is assumed to be the process id from some former call to Er.spawn. This call sends a message with the keys “Hello” and “From”. Hello is a Date object with the current date, and From is the process id of the current process, which can always be fetched with Er.pid(). Passing the current pid means the myPid process can send us messages in return, since we’ve told it who we are.
When myBackgroundFunction wants to read off its message queue, it calls the Er.receive function, telling it the kind of message it’s interested in, and a function to call when such a message is received. Interest in a message is expressed using a message pattern which, just like the messages themselves, is a simple hash table.
yield Er.receive({ Hello: Date, From: _ }, // pattern
function(msg) { // handler
log("Hello=" + msg.Hello);
log("From=" + msg.From);
});
This matches any message in the current process’s queue which has a Hello key with a Date object as the value, and with a From key with any value. Explicit value matching for number and string literals and object references is also possible. The “_” for the From key means that any value is accepted. There are a few other matching rules as well that make this a very powerful but simple message dispatching mechanism.
If a message matches the pattern, it is passed to the handler function specified in the following argument to Er.receive. The handler can look up the key values it needs in order to act on the message. It can also send messages to other processes, spawn new processes, receive queued messages or perform other work.
Because the Er.receive call doesn’t return until a message matching one of patterns is received and handled, we put a yield in front of the call to avoid blocking.
When a process finishes or exits, it automatically sends a message to any processes which link to it. Linking is done by passing a pid to Er.link. The sent message is of the form:
{ Signal: Er.Exit, From: exiting_pid, Reason: reason }
The Reason value comes either from the exiting process calling Er.exit(reason), or just throwing the reason as an exception. If the linking process does not handle this message, it will exit itself, sending exit messages to its own linked processes. This allows for simple process chaining and failure handling.
Processes can also register to receive messages sent to a given name string, using Er.register(name). Registered names can be passed as the first argument to Er.send. Multiple processes can register for the same name, and they will all receive a message sent to that name, allowing for simple multi-casting.
To see Erjs in action, check out the example page, and view source. Er.js itself can be found here.
-
WOW!!!
-
Nice. What mainstream browsers support javascript 1.7?
Safari 3 doesn’t seem to, but Firefox 2 does (and looks very nice).
Takes me back to the co-operative multitasking of Windows 3
-
Terrific! I’ve always thought there was a symmetry between generators and Erlang processes just waiting to be explored. Glad to see you exploring that realm.
Generators are currently only Firefox 2+, but they’re also included in the upcoming ES4 (ECMAScript 4, aka JavaScript 2) spec. I’d expect to see them showing up in more browsers within the next year or two.
-
verrrrrrrrrrrrrrry cool
-
Alex, I am trying to do that: http://erlyjs.googlecode.com (a JS compiler for the Erlang VM)
Comments are now closed.

9 comments