How would I design an API to hide the asynchronous nature of AJAX and HTTP requests, or basically delay it to provide a fluent interface. To show an example from Twitter\'s
Give a look to the following article published just a couple of days ago by Dustin Diaz, Twitter Engineer on @anywhere:
He talks about a really nice technique that allows you to implement a fluent interface on asynchronous methods, basically methods chained together independent of a callback, using a really simple Queue implementation.
I'm developing FutureJS which was originally based on Crockford's promises (original slides). The current goal is to be the Async Toolbox of JavaScript and eliminate chaining clutter.
Asynchronous method queueing allows you to chain actions on data which may or may not be readily available. This is how Twitter's @Anywhere api works.
You might want a model which remotely fetches data in this fashion:
Contacts.all(params).randomize().limit(10).display();
Contacts.one(id, params).display();
Which could be implemented like so:
var Contacts = Futures.chainify({
// Providers must be promisables
all: function(params) {
var p = Futures.promise();
$.ajaxSetup({ error: p.smash });
$.getJSON('http://graph.facebook.com/me/friends', params, p.fulfill);
$.ajaxSetup({ error: undefined });
return p.passable();
},
one: function(id, params) {
var p = Futures.promise();
$.ajaxSetup({ error: p.smash });
$.getJSON('http://graph.facebook.com/' + id, params, p.fulfill);
$.ajaxSetup({ error: undefined });
return p.passable();
}
},{
// Consumers will be called in synchronous order
// with the `lastResult` of the previous provider or consumer.
// They should return either lastResult or a promise
randomize: function(data, params) {
data.sort(function(){ return Math.round(Math.random())-0.5); // Underscore.js
return Futures.promise(data); // Promise rename to `immediate`
},
limit: function(data, n, params) {
data = data.first(n);
return Futures.promise(data);
},
display: function(data, params) {
$('#friend-area').render(directive, data); // jQuery+PURE
// always return the data, even if you don't modify it!
// otherwise your results could be unexpected
return data;
}
});
Things to know:
providers - promisables which return dataconsumers - functions which use and or change data
dataundefined (or not returning anything) the next method in the chain will use the defined objectcontext - apply()d to each provider and consumer, thus becoming the this objectparams - reserved for future useAlternatively you could use synchronous callback chaining - what you may have seen elsewhere as chain().next() or then():
Futures.sequence(function(callback) {
$.getJSON("http://example.com", {}, callback);
}).then(function(callback, result, i, arr) {
var data = transform_result(result);
$.getJSON("http://example.com", data, callback);
}).then(...)
I named it sequence rather than chain since _.js already has a method named chain and I'd like to use _.methodName for my library as well.
Take a peek and let me know what you think.
FuturesJS will work alongside jQuery, Dojo, etc without issue. There are no dependencies. It will work with Node.js (and Rhino when using env.js).
=8^D
P.S. As to the ORM / MVC fix - you can check out JavaScriptMVC and SproutCore. I'm also working on my own solution called TriforceJS, but I don't have anything ready for release yet.
P.P.S Example of promisables
var doStuff = function (httpResult) {
// do stuff
},
doMoreStuff = function (httpResult) {
// do more stuff
};
function fetchRemoteData(params) {
var promise = Futures.promise();
$.getJSON("www.example.com", params, promise.fulfill, 'jsonp');
return promise;
}
p = fetchRemoteData(params);
p.when(doStuff);
p.when(doMoreStuff);
The AJAX synchronous issue, I believe, has already been abstracted away by libraries such as jQuery (i.e. its ajax call which allows you to specify async or synch operation through the async property). The synchronous mode, if chosen, hides the asynchronous nature of the implementation.
jQuery is also an example of a fluent interface and chaining. There are other libraries that do the same. Saves you reinventing the wheel - gets you rolling right away with what you are looking for.
If this works as an answer then you get some good automatic browser compatibility across these features. That stuff takes a long while to build out from scratch.
I see Twitter's new Anywhere API notes jQuery - maybe everything is already there if you do some digging.