Er.js piggybacks on Neil Mix’s Thread.js which fakes threading in JavaScript 1.7 using coroutines and nested generator continuations. The goal is to replicate Erlang’s concurrent lockless process model and message-passing APIs in JavaScript.
Running a JavaScript function in the background is easy with Er.js:
Er.spawn(myBackgroundFunction);
Er.spawn starts a new Er.js 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 Er.js 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 Er.js, 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 in the process queue matches a pattern passed to
Er.receive, it is passed to the handler function found in the
argument list following the pattern. 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 finish until a message
matching one of patterns is received and handled, we put a yield in
front of the call to avoid blocking other processes.
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 throw'ing 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.
Er.js 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.
Going forward, Er.js will wrap certain long-running asynchronous JavaScript calls in synchronous yielding wrappers so that processes can avoid convoluted asynchronous code.
XmlHttpRequest, AJAX and JSON should integrate nicely with the process and
message-passing model. This will allow processes to avoid asynchronous
callbacks, instead using Er.send and Er.receive as
with other messages. For example:
rpc = Er.spawn("http://www.beatniksoftware.com/echo");
Er.send(rpc, { Echo: "This is the echo payload" });
yield Er.receive({ EchoResponse: String },
function (msg) {
alert("Got Echo response: " + msg.EchoResponse);
});
For DOM Events, this might be embodied with the following, where
"button1" below could alternately be an element id, DOM node, CSS
selector, etc:
Er.link_event("button1", "click");
yield Er.receive({ Signal: Er.Event, From: _, Event: "click" },
function (msg) {
alert("Got Click event from: " + msg.From);
});
It would also be interesting to explore the use of native threads for running processes, leveraging e.g. Google Gears' WorkerPools if available on the client.
Er.js is from Alex Graveley (www).