Проектирование свободного интерфейса 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 (т.е.