Diseñando una interfaz Javascript fluida para abstraer la naturaleza asincrónica de AJAX
asynchronous fluent-interface (3)
¿Cómo diseñaría una API para ocultar la naturaleza asíncrona de las solicitudes AJAX y HTTP, o básicamente la demoraría para proporcionar una interfaz fluida? Para mostrar un ejemplo de la nueva API Anywhere de Twitter:
// get @ded''s first 20 statuses, filter only the tweets that
// mention photography, and render each into an HTML element
T.User.find(''ded'').timeline().first(20).filter(filterer).each(function(status) {
$(''div#tweets'').append(''<p>'' + status.text + ''</p>'');
});
function filterer(status) {
return status.text.match(/photography/);
}
vs this (naturaleza asincrónica de cada llamada es claramente visible)
T.User.find(''ded'', function(user) {
user.timeline(function(statuses) {
statuses.first(20).filter(filterer).each(function(status) {
$(''div#tweets'').append(''<p>'' + status.text + ''</p>'');
});
});
});
function filterer(status) {
return status.text.match(/photography/);
}
Encuentra al usuario, obtiene su línea de tiempo de tweets, filtra solo los primeros 20 tweets, aplica un filtro personalizado y finalmente utiliza la función de devolución de llamada para procesar cada tweet.
Supongo que una API bien diseñada como esta debería funcionar como un generador de consultas (piense en ORM) donde cada llamada de función construye la consulta (HTTP URL en este caso), hasta que golpea una función de bucle como cada / map / etc., la llamada HTTP se realiza y la función que se transfiere se convierte en la devolución de llamada.
Una ruta de desarrollo fácil sería hacer que cada llamada AJAX sea sincrónica, pero probablemente esa no sea la mejor solución. Estoy interesado en encontrar una manera de hacerlo asíncrono, y aún ocultar la naturaleza asincrónica de AJAX.
Eche un vistazo al siguiente artículo publicado hace apenas un par de días por Dustin Diaz, Ingeniero de Twitter en @anywhere:
Él habla de una técnica realmente agradable que le permite implementar una interfaz fluida en métodos asíncronos, básicamente métodos encadenados de forma independiente de una devolución de llamada, utilizando una implementación de Queue realmente simple.
El problema síncrono AJAX, creo, ya ha sido abstraído por bibliotecas como jQuery (es decir, su llamada ajax que le permite especificar la operación asincrónica o sincronizada a través de la propiedad async ). El modo sincrónico, si se elige, oculta la naturaleza asincrónica de la implementación.
jQuery es también un ejemplo de una interfaz fluida y de encadenamiento. Hay otras bibliotecas que hacen lo mismo. Le ahorra reinventar la rueda, lo pone en marcha de inmediato con lo que está buscando.
Si esto funciona como una respuesta, entonces obtienes una buena compatibilidad automática del navegador entre estas características. Eso lleva mucho tiempo construir desde cero.
Veo las nuevas notas de la API en cualquier lugar de Twitter, jQuery, tal vez todo ya está allí si lo haces un poco de excavación.
Estoy desarrollando FutureJS, que originalmente estaba basado en las promesas de Crockford ( diapositivas originales ). El objetivo actual es ser la caja de herramientas Async de JavaScript y eliminar el desorden de encadenamiento.
Futures.chainify (proveedores, consumidores, contexto, params)
La cola de métodos asíncronos le permite encadenar acciones en datos que pueden o no estar fácilmente disponibles. Así es como funciona la API @Anywhere de Twitter.
Es posible que desee un modelo que obtenga datos de esta manera de manera remota:
Contacts.all(params).randomize().limit(10).display();
Contacts.one(id, params).display();
Que podría implementarse así:
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;
}
});
Cosas que saber:
-
providers
- promisibles que devuelven datos -
consumers
: funciones que usan y / o cambian datos- el primer argumento debe ser
data
- al devolver un material promisable, el siguiente método de la cadena no se ejecutará hasta que se cumpla la promesa
- al devolver un "objeto literal", el siguiente método de la cadena usará ese objeto
- al regresar
undefined
(o no devolver nada) el siguiente método de la cadena usará el objeto definido
- el primer argumento debe ser
-
context
-apply()
d a cada proveedor y consumidor, convirtiéndose así enthis
objeto -
params
- reservado para uso futuro
Alternativamente, podría usar el encadenamiento de devolución de llamada sincrónico, lo que puede haber visto en otro lugar como chain (). Next () o 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(...)
Lo llamé sequence
lugar de chain
porque _.js ya tiene un método llamado chain
y también me gustaría usar _.methodName para mi biblioteca.
Echa un vistazo y dejame saber lo que piensas .
FuturesJS trabajará junto con jQuery, Dojo, etc. sin problema. No hay dependencias Funcionará con Node.js (y Rhino al usar env.js).
= 8 ^ D
PD En cuanto a la solución ORM / MVC, puede consultar JavaScriptMVC y SproutCore . También estoy trabajando en mi propia solución llamada TriforceJS, pero todavía no tengo nada listo para su lanzamiento.
Ejemplo de PPS de 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);