DailyJS

Let's Make a Framework: Ajax Part 2

Alex R. Young

Subscribe

@dailyjs

Facebook

Google+

tutorials frameworks lmaf ajax

Let's Make a Framework: Ajax Part 2

Posted by Alex R. Young on .
Featured

tutorials frameworks lmaf ajax

Let's Make a Framework: Ajax Part 2

Posted by Alex R. Young on .

Welcome to part 14 of Let's Make a Framework, the ongoing series about
building a JavaScript framework. This part continues discussing Ajax.

If you haven't been following along, these articles are tagged with
lmaf. The project we're creating is called Turing and is available on GitHub:
turing.js.

Cross-Domain Requests

Cross-domain requests are useful because they can be used to fetch data
from services like Twitter. This is now a popular technique, despite
feeling clunky to implement. This is another case where lack of browser
features can be patched by JavaScript.

Implementations in the Wild

The Glow framework has
glow.net.xDomainGet and glow.net.loadScript.
These are similar, but place different requirements on the server. What
loadScript does is called JSONP. jQuery also implements
JSONP, and has this to say in the documentation:

The jsonp type appends a query string parameter of callback=? to the URL. The server should prepend the JSON data with the callback name to form a valid JSONP response. We can specify a parameter name other than callback with the jsonp option to \$.ajax().

A JSONP URL looks like this:

http://feeds.delicious.com/v1/json/alex_young/javascript?callback=parseJSON

The reason a callback is specified in the URL is the cross-domain Ajax
works by loading remote JavaScript by inserting script
tags, and then interpreting the results. The term JSONP comes from JSON
with Padding
, and originated in Remote JSON -
JSONP

by Bob Ippolito.

The Algorithm

JSONP works like this:

  1. The framework transparently creates a callback for this specific
    request
  2. A script tag is inserted into a document. This is of course
    invisible to the user
  3. The src attribute is set to
    http://example.com/json?callback=jsonpCallback,
  4. The server generates a response
  5. The server wraps its JSON response like this:
    jsonpCallback({ ... })
  6. Once the script has loaded, the callback is called, and the client
    code can process the JSON accordingly
  7. Finally, the callback and script tags are removed

As you can see, servers have to be compliant with this technique -- you
can't use it to fetch arbitrary JSON or XML.

API Design

I've based this on the Glow framework, and the option names are
consistent with the other turing.net code:

turing.net.jsonp('http://feeds.delicious.com/v1/json/alex_young/javascript?callback={callback}', {
  success: function(json) {
    console.log(json);
  }
});

The {callback} string must be specified -- it gets replaced
by the framework transparently.

Implementation

To create a callback, no clever meta-programming is required. It's
sufficient to create a function and assign as a property on
window:

methodName = '__turing_jsonp_' + parseInt(new Date().getTime());
window[methodName] = function() { /* callback */ };

A script tag is created and destroyed as well:

scriptTag = document.createElement('script');
scriptTag.id = methodName;

// Replacing the request URL with our internal callback wrapper
scriptTag.src = url.replace('{callback}', methodName);
document.body.appendChild(scriptTag);

// The callback should delete the script tag after the client's callback has completed
document.body.removeChild(scriptTag)

That's practically all there is to it. You can see the real
implementation in
turing.net.js by searching for JSONPCallback.

Conclusion

Although JSONP requires server support, a wide variety of interesting
services support it. This simple technique has made cross-domain
requests popular -- how many blogs and sites now feature live Twitter
feeds written with pure JavaScript?