JavaScript
Article
By Harry Fuecks

Eliminating async Javascript callbacks by preprocessing

By Harry Fuecks

The Catch 22 of AJAX is, for the sake of an easy life, most of the time we want to write “synchronous code” but asynchronous is the only way to avoid some rather nasty usability issues. This means rather than being able to write simple code, as we’d like to, such as;


function doClick() {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET","http://example.com/products",false);

    # Execution blocks here, waiting for the response to complete...
    xmlhttp.send(null);
    alert(xmlhttp.responseText);
}

…we’re required instead to handle this via callbacks, the simplest example being…


var xmlhttp = new XMLHttpRequest();

function doClick() {
    xmlhttp.open("GET","http://example.com/products",true);

    // Set the callback
    xmlhttp.onreadystatechange = handleResponse;
    xmlhttp.send(null);
}

function handleResponse() {
    if ( xmlhttp.readyState == 4 ) {
        alert (xmlhttp.responseText);
    }
}

…but that’s now introduced a whole load more potential issues. The callback now relies on the global xmlhttp object being available (and globals for any significant size project are generally evil). And what if the user keeps firing that doClick() function? What about async requests that pause for a coffee break then return unexpected much later (timeouts required)? And that’s just for starters.

Anyway – a couple of interesting projects are working on giving us the best of both worlds – asynchronous requests but (what looks like) blocking code. Both work on the basis of extending Javascript itself, so that what before was a massive effort in manual coding becomes neatly hidden behind a new Javascript operator or keyword.

Narrative Javascript

The first is Narrative Javascript which adds a new “blocking operator” -> so that your code becomes something like;


function doClick() {
    # Note the blocking operator...
    var response = doHttpRequest->("http://example.com/products");
    alert(response);
}

The Narrative JS overview makes a good starting point. Perhaps an advantage of Narrative JS is it’s pure Javascript – although the docs advise preprocessing offline using something like Rhino, you probably could (in theory) preprocess your code on demand (at a potentially significant performance cost) in the browser, the Javascript parser being narcissus (also pure JS). At the very least, being all JS is likely to make people more confident about using it.

--ADVERTISEMENT--

jwacs

The second is jwacs – Javascript With Advanced Continuation Support. This actually goes a fair bit further than just being able to simulate blocking code, adding four new keywords (and an import statement). The earlier example (with aide of a bundled jwacs utility API) becomes;


function doClick() {
    var response = JwacsLib.fetchData("GET", "http://example.com/products"))
    alert(response);
}

To see the extended Javascript, you need to look at the above fetchData definition;


  fetchData: function(method, url)
  {
    var http = JwacsLib.getHttpObj();
    var k = function_continuation;

    http.onreadystatechange = function()
    {
      try
      {
        // Report results to the continuation on completion
        if(http.readyState == 4)
        {
          // Check for errors
          if(!(http.status == undefined ||
               http.status == 0 ||
               (http.status >= 200 && http.status < 300)))
          {
            var err = new Error("Server returned " + http.status);
            throw err;
          }

          // No errors, so resume the continuation with the raw results
          http.onreadystatechange = JwacsLib.emptyFunction;
          resume k <- http.responseText;
        }
      }
      catch(e)
      {
        // Errors are thrown as exceptions into the continuation
        http.onreadystatechange = null;
        throw e -> k;
      }
    };

    http.open(method, url);
    http.send(null);
    suspend;
  }


Note the function_continuation, suspend, resume and extended throw: throw e -> k; above. The jwacs preprocessor is written in LISP

So what’s the general feeling here? Would you consider using these?

The very notion of extending Javascript with new syntax may be, to many, offensive. You’ve also introduced some significant dependencies – a later change of plan could lead to significant re-writes (and of course they’re both still very much prototype).

At the same time, writing anything non-trivial in Javascript involving asynchronous processing and callbacks can quickly become a nightmare – why not eliminate human effort with some smart syntax? The general approach here seems good to me.

webtuesday

While I’m here – a quick ad for webtuesday tonight with Patrice talking about his experiences web testing with Selenium (it’s not just “more Java” you know ;) at tilllate’s HQ.

Recommended
Sponsors
The most important and interesting stories in tech. Straight to your inbox, daily. Get Versioning.
Login or Create Account to Comment
Login Create Account