AJAX over WebSockets

AJAX over WebSockets

In this tutorial, we show how to easily run AJAX over WebSockets for browser to server communication. We also go over situations where AJAX should not be used and when real time messages sent over a WebSocket connection is preferred over using AJAX.

You are probably thinking why would I want to run AJAX over WebSockets. There are several benefits to running AJAX over WebSockets, such as:

  • Less overhead, thus much faster (including less overhead than HTTP/2).
  • Much easier to implement stateful services, if needed.
  • Both AJAX and asynchronous WebSocket data can be multiplexed over the same connection.
  • Both text frames, used by JSON, and binary frames can be multiplexed over the same connection.
  • Does not require an AJAX JavaScript library (*).

(*) Few developers directly use the browser's antiquated and difficult to use XmlHttpRequest object, which is required for any type of AJAX interaction. Instead, they rely on AJAX libraries, such as the AJAX library in JQuery, which shields the developer from directly using the XmlHttpRequest object. The WebSocket API on the other hand is very easy to understand and use; thus, developers can easily take advantage of the WebSocket API without requiring a JavaScript library to simplify the programming.

AJAX stands for Asynchronous JavaScript and XML, but it really just refers to using any kind of asynchronous HTTP call from a web front-end to the server back-end. In fact most developers use JSON these days since XML is bulky and adds a lot of overhead.

A server back-end may provide many AJAX services, and to differentiate one service from another, each service may have its own URL. In other words, most modern AJAX calls are to REST APIs using JSON as the data serialization format. As an example, we may have a service 'math' that provides a set of mathematical services such as 'add' and 'subtract'. The 'add' service may use an URI such as "/math/add" and the 'subtract' service may use an URI such as "/math/subtract".

AJAX returns a single response to a single request. However, AJAX is asynchronous; thus, a web front-end can send any number of AJAX requests without having to wait for each response.

WebSockets on the other hand can handle multiple bidirectional messages between the browser and the server. There is no correlation between the messages sent in either direction. It is up to the application to interpret and manage these messages. If you have any TPC/IP socket experience, you can simply think of WebSockets as a TCP stream that includes a packet size header. Each WebSocket message is sent and received as one packet.

Implementing AJAX on a WebSocket connection is conceptually very easy:

  1. The data is encoded as JSON.
  2. The JSON data is sent to the server over the WebSocket connection.
  3. The server reconstructs the data from the JSON string.
  4. The server finds the AJAX service and calls the service.
  5. Response data from the AJAX service is encoded as JSON.
  6. The server sends the JSON response data to the browser via the WebSocket connection.
  7. The AJAX response is managed in the browser.

AJAX over WebSockets JavaScript library

To simplify the coding, we wrap the client side logic into an easy to use JavaScript library. There are a few AJAX specific features we need to think about when designing this library. As we mentioned above, AJAX is asynchronous on the client side, thus many AJAX requests can be in-flight at the same time. Each AJAX request is associated with a JavaScript callback that gets called when the server sends the response. We must find a way to uniquely identify each in-flight request such that each AJAX response triggers the correct callback function. The following figure depicts three asynchronous and in-flight AJAX requests:

AJAX in flight

Figure 1: Three asynchronous in-flight AJAX requests

Each of the asynchronous responses A', B', and C' must trigger the correct response callback function for the request A, B, and C, even if the response messages should be in a different order.

The easiest way to implement the client side AJAX callback handling is to store each callback in an object and give each callback a unique ID. The ID, which can for example be a number, is sent to the server as part of the message. The server side then bundles this ID with the response data.

When the client receives the AJAX response, the client finds the callback function by using the ID received from the server. The callback IDs need to be unique for each request/response pair. A super easy way to find a unique ID is to use a random number and look for collisions.

1
2
3
4
5
6
7
8
9
10
var callbacks={} // saved AJAX callbacks: key=id, val=function
 
function saveCallback(callback) {
    var rpcID; // Find a unique ID
    do {
        rpcID=Math.floor(Math.random() * 100000);
    } while(callbacks[rpcID]); // while collisions
    callbacks[rpcID]=callback; // Save callback, where rpcID is the key.
    return rpcID; // The ID sent to the server, where it is echoed.
}

Using the AJAX over WebSockets JavaScript library

Say we have a math AJAX service, where we can add two numbers:

1
function math.add(a, b) => return a+b

To call this from the client, we must provide the two arguments a and b, the callback function, and the math REST URI.

The API we designed for our AJAX over WebSocket client JavaScript library requires that the two arguments 'callback' and 'REST URI' are provided as the two first arguments. For example, the math.add(a,b) function must be called from the client as follows:

1
ajax(callback, "math/add", a, b)

The AJAX response callback receives two arguments, the response data and possibly an error message. The response data 'rsp' is null if the error argument is provided.

1
function myCallback(rsp,err)

For JavaScript Ninjas:

If you are a JavaScript developer, you are probably wondering why we use the antiquated asynchronous callback design pattern and not the new JavaScript Promise object. The callback API may be easier to understand for new JavaScript developers than the new Promise object, however we have included an additional library and example using the new Promise API. Here is an example on how to use this API:

1
2
3
4
var ajax=createAjax('service.lsp', .....);
ajax("math/add", a, b).
    then(function(rsp) { console.log("Result: " + rsp); }).
    catch(function(err) { console.log("Error: " + err); });

Download:

The AJAX over WebSocket client library is implemented in a mere 22 lines of code. To get this code and a fully working example, including the server code, download our AJAX over WebSocket Example.

Download AJAX over WebSocket Example


Run the Ajax over WebSockets Example

When not to use AJAX

The 'Mako Server' and the 'Barracuda App Server library' are typically used for device management and our focus is on these type of systems. For device management, the most obvious reason for not using AJAX is when the solution requires real time data pushed from the server to the client. AJAX can be used for polling new data, but it is much easier to simply push the real time data over a WebSocket connection.

Another reason for not using AJAX is much more subtle and may not be discovered until the complete solution is designed, resulting in expensive re-design if not properly thought of during the initial design phase. As an example, let's assume we need to design a solution for controlling one light bulb. The light switch state is presented in the browser as either on or off. AJAX may seem like the perfect candidate for such a simple solution. The user clicks the light switch in the browser, an AJAX request is sent to the server, the server turns the light bulb on or off, and the AJAX response is sent to the browser. However, there is a subtle problem with this solution -- the web is a multi user system enabling multiple users to surf to the same web server. In other words, the server (and the light bulb controlled by the server) is a shared resource that may be controlled by many users. What happens if two users try to turn the light bulb on or off at the same time? The two browser interfaces displaying the light switch state will be out of synch and not show the real on/off state for the light bulb. For this reason, AJAX is not a good solution for such a system.

Instead of using AJAX, we can take full advantage of WebSocket's bi-directional features and design a reactive system that encompasses the complete solution, including all connected browsers, and the server. The following figure depicts such a reactive system, where the light switch click is propagated to the server and then to all connected browsers.

Reactive multi user solution

Figure 2: Reactive multi user solution

WebSockets lets us respond to user inputs in real time and reactively update all connected browser user interfaces simultaneously.

For additional reading and a fully functional reactive multiuser light bulb controller example, see the two following tutorials:

Posted in Tutorials