Hi from Oni Labs!

April 21, 2010 by Oni Labs

As our web page says, we are developing a platform for real-time web applications. Why another platform, you may ask? Well, we think what makes Oni unique is that we build on one language on both the client and on the server: JavaScript. But it's not quite the JavaScript you know, it's JavaScript with a twist.

If you've ever worked on a complex web application, we don't need to tell you that dealing with event-based, asynchronous control flow is hard. When you juggle too many concurrent XMLHttpRequests, user interactions, timeouts, call failures and cancellations, you're bound to end up with an unmaintainable tangled mess of callback code sooner or later.

It's tempting to think that threads or web workers are the answer, but these throw even more concurrency into the mix... concurrency that needs to be coordinated at some point in your application.

Like many computer scientists and software engineers, we at Oni Labs have been thinking for some time that a fundamental shift in addressing concurrency is overdue.

This is why we developed Stratified JavaScript (SJS). We took cues from some more esoteric academic research in the area of orchestration languages (such as Orc). We then designed SJS to implement concurrency in a composable, bottom-up way. SJS allows you to formulate hairy asynchronous code in a straightforward, structured sequential style.

Best of all, with SJS you don't have to learn a completely new language. SJS is just normal JavaScript with some new "stratified" constructs that embed naturally into the familiar core language.

SJS works in all major browsers and also server-side in our Oni Rocket JS web application server.

In upcoming blog posts we'll examine some of SJS's features in detail, but in the meanwhile, here is a short 6 minute video of Tom showing how to use SJS in the Rocket server to create a simple multiuser chat application.

Thank you for stopping by :-)

The server-side chat.api file

var cluster = require("cluster");

var chat = cluster.createBroadcaster(api, {backlog: 10});

api.exports.connect = function () {
  return chat.register(this);
};

The client-side chat.app file

var s = require("surface");
var b = require("bridge");

var main = s.VBox({w:"*", h:"*"}, 
  s.VBox({w:"*", class: "surface-title"}, s.Label({w:"|"},"Chat")),
  s.VBox({h:"*", w:"*", name: "messages"}),
  s.HBox({w:"*", class: "surface-toolbar", name: "toolbar"}, 
    s.TextInput({w:"*", style: "margin: 5px", name: "text"})
  )
);

s.surface.append(main);
var chat = b.call("chat.connect");

waitfor {
  while (true) {
    var message = chat.waitForMessage();
    main.messages.append(s.Label(message));
  }
} or {
  while (true) {
    main.toolbar.text.waitFor("keydown", function (ee) {
      return ee.event.keyCode == 13;
    });
    chat.broadcast(main.toolbar.text.getValue());
    main.toolbar.text.setValue("");
  }
}