Bringing realtime to your web applications

Few years ago only lazy didn’t say about bringing desktop application experience to the web ones. However in reality, it just meant that user actions didn’t always required page reload, but could change page UI dynamically. As for other application events, as a rule they were not handled dynamically.

Well, now you can find more and more web applications acting really like desktop ones. For example, Facebook and G+ have widgets which update automatically. You can keep the page untouched, but you will see anyway new status updates appear as your friends submitting. The same for notifications. Whenever a new one arrives, the counter changes and the notification list extends.

That seems to me as a trend worth to follow. I’ve been thinking of a solution to vivify widgets on my sites. Now I’m coming up with the following pattern.


Going real time

Ok, we have various widgets on a page and want them update in real-time. We can have a message broker object ($.rta) with which we subscribe our widgets for update events.

var aWidget = (function() {
    return { 
        name : 'aWidget',
        module : 'aModule',
        init : {            
            $.rta.subscribe(this, function(e, data){ });
        }
    }
}());

Since all my widgets are derived by $.jsa.WidgetAbstract (Please find details in JS Application Design , I extended the class by $.jsa.RealTimeWidgetAbstract, which made the following interface:

var aWidget = function(settings) {
return $.jsa.extend({
        name : 'aWidget',
        module : 'aModule',
        subscribeRta : function(e, data) {}

      }, $.jsa.RealTimeWidgetAbstract, settings);
};

Pretty easy, is not it? Well, but the message broker is assumed to communicate somehow with the server. There is a bunch of trick called COMET and couple of HTML5 approaches (Server-Sent Events and WebSockets). All can serve, so you find examples and comparison in my article WebSockets vs Server-Sent Events vs Long-polling . In this particular case I’m using long-polling, but leaving place for any other implementation. You can plug it in as an adapter manually.


$.rta.communicationBridge = function(adapter) {
    var _private = {       
        communicationBridge : null,
        adapter : {
          'worker' : (function() {
              // The worker is encopsulated into worker.rta.js to show the experiment code
              // more explicitly. However you can keep worker code here. Find details in
              // the section Inline Workers at http://www.html5rocks.com/en/tutorials/workers/basics/
              var _adapter = new Worker('./js/worker.rta.js');
              return {
                  subscribe : function(callback) {
                    _adapter.addEventListener('message', callback, false);
                  },
                  publish : function(message) {
                     message.query = 'method=worker&queue=' + message.query;
                     _adapter.postMessage(message);
                  },
                  terminate : function() {
                     _adapter.postMessage({action: 'close'});
                  }
              }
          })(),
          'sse' : function() {
              // Here you can put the code of Server-Sent Events connector
          }
        }
    }    
    _private.communicationBridge = _private.adapter[adapter];
    // As browser window is closed, connection is closed as well
    $(window).unbind('unload').bind('unload', function() {
       _private.communicationBridge.terminate();
    });
    return {
        subscribe: function(callback) {
            _private.communicationBridge.subscribe(callback);
        },
        publish : function(message) {
            _private.communicationBridge.publish(message);
        }
    }
};

Long-polling is no real full-duplex asynchronous messaging channel, but a trick based on ability of XMLHttpRequest to wait until the server eventually responds. That is an emulation of server pushing events to the browser. Thus, we don’t have open channels, but make a long-waiting request, passing the message queue onto the server controller. The controller serves the queue in a loop until an update for any of queued widgets is met. The RTA communication bridge gets update data, fires the event for the updated widget and repeats the request. To make it true, when subscribing widgets we actually populate the message queue and start requesting the server only when the queue is complete by $.rta.connect("path to the controller script");

Ok, we have widgets objects and the broker, which is supposed to run on background. It can be done by using a web worker. Let’s put our relatively resource-intensive functionality which makes long-polling requests into a worker. So it will be a “black-box” to which we provide once message queue as a parameter query and controller path (everything for the post request) and keep receiving updates back through the listener function.


var request = {}, iteration = 1, DELAY = 3000;
var cEventSource = function() {    
    return (function() {    
    var _xhr = new XMLHttpRequest();
    _xhr.onreadystatechange = function() {
        if (_xhr.readyState === 4 && _xhr.status == 200 && _xhr.responseText.length) {
            var data = JSON.parse(_xhr.responseText);
            self.postMessage(data);
            setTimeout(cEventSource, DELAY);
        }
    };
    _xhr.open("POST", request.eventSource, true);
    _xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    _xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    _xhr.send(request.query);
    iteration ++;
    })();
};
self.onmessage = function(event) {
    request = event.data;
    if (event.data.action == 'close') { return self.close(); }
    cEventSource();
};

What about the demo code

You can find an implementation demonstrating the described architecture here demo.dsheiko.com/rta. There are two widgets updating real-time and console showing as the server pushes update data back to the application. It is supposed to be any sort of backend architecture. For this sample I wrote a micro-framework, which will help to show the idea explicitly, I hope. To make a widget whilst page is being generated, you just render the module respectively. Modules logic (models) is located in Module folder and their representations in Template folder. Index.php uses renderModule method of RTA\App class to build widgets. As a module is rendered (update), its modification time is stored into session.


When page is ready, demo.js subscribes all the widgets to RTA and establishes connection to facadeController.php. The controller receives information about which widgets subscribed for update and which modules they are associated. The controller is checking each module for an update ((RTA\App::getModuleData)) in a loop. Module previous modification time is used as a filter by module model. As an update is encountered, the controller retrieves HTML of updated module (RTA\App::renderTemplate) responds back to RTA.


Demo: http://demo.dsheiko.com/rta/

Source code: http://code.google.com/p/realtime-architecture-framework/