Проектирование свободного интерфейса Javascript для абстрагирования асинхронного характера AJAX
Как мне создать API, чтобы скрыть асинхронный характер запросов AJAX и HTTP, или в основном задержать его, чтобы обеспечить свободный интерфейс. Чтобы показать пример из Twitter нового Anywhere API:
// get @ded 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 (асинхронный характер каждого вызова хорошо виден)
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/);
}
Он находит пользователя, получает свою временную шкалу твитов, фильтрует только первые 20 твитов, применяет настраиваемый фильтр и в конечном итоге использует функцию обратного вызова для обработки каждого твита.
Я предполагаю, что хорошо подобранный API, подобный этому, должен работать как построитель запросов (думаю, ORM), где каждый вызов функции строит запрос (HTTP-URL в этом случае) до тех пор, пока он не столкнется с функцией циклирования, такой как each/map/и т.д., выполняется HTTP-вызов, и передаваемая функция становится обратным вызовом.
Простым маршрутом разработки было бы сделать каждый вызов AJAX синхронным, но это, вероятно, не лучшее решение. Мне интересно выяснить способ сделать его асинхронным и по-прежнему скрывать асинхронный характер AJAX.
Ответы
Ответ 1
Посмотрите на следующую статью, опубликованную всего пару дней назад Дастином Диасом, инженером Twitter по адресу @anywhere:
Он говорит о действительно хорошем методе, который позволяет вам внедрять свободный интерфейс для асинхронных методов, в основном методов, связанных друг с другом независимо от обратного вызова, используя действительно простую реализацию Queue.
Ответ 2
Я разрабатываю FutureJS, который первоначально был основан на Crockford promises (оригинальные слайды). Нынешняя цель - использовать Async Toolbox JavaScript и устранить беспорядочный беспорядок.
Futures.chainify(поставщики, потребители, контекст, параметры)
Очередь асинхронных методов позволяет вам группировать действия над данными, которые могут быть или не быть доступны.
Вот как работает Twitter @Anywhere api.
Вам может понадобиться модель, которая дистанционно извлекает данные таким образом:
Contacts.all(params).randomize().limit(10).display();
Contacts.one(id, params).display();
Что можно реализовать так:
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;
}
});
Что нужно знать:
-
providers
- многозначные данные, возвращающие данные
-
consumers
- функции, которые используют или изменяют данные
- первый аргумент должен быть
data
- при возврате обещаемого следующего метода в цепочке не будет выполняться до тех пор, пока обещание не будет выполнено
- при возврате "литерального объекта" следующий метод в цепочке будет использовать этот объект
- при возврате
undefined
(или не возвращении чего-либо) следующий метод в цепочке будет использовать определенный объект
-
context
- apply()
d каждому провайдеру и потребителю, став тем самым объектом this
-
params
- зарезервировано для будущего использования
В качестве альтернативы вы можете использовать синхронную цепочку обратного вызова - что вы, возможно, видели в другом месте как chain(). next() или 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(...)
Я назвал его sequence
, а не chain
, поскольку у _.js уже есть метод с именем chain
, и я бы хотел использовать _.methodName для моей библиотеки.
Возьмите взгляд и дайте мне знать, что вы думаете.
FuturesJS будет работать вместе с jQuery, Dojo и т.д. без проблем. Никаких зависимостей нет. Он будет работать с Node.js(и Rhino при использовании env.js).
= 8 ^ D
P.S. Что касается исправления ORM/MVC, вы можете проверить JavaScriptMVC и SproutCore. Я также работаю над своим собственным решением под названием TriforceJS, но у меня пока нет готового релиза.
P.P.S Пример 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);
Ответ 3
Синхронная проблема AJAX, я считаю, уже была отвлечена библиотеками, такими как jQuery (т.е.