设计一个流畅的Javascript接口来抽象出AJAX的异步特性

Anu*_*rag 19 javascript ajax asynchronous fluent-interface

我如何设计一个API来隐藏AJAX和HTTP请求的异步性质,或者基本上将其延迟以提供流畅的界面.要在Twitter的新Anywhere API中显示示例:

// 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/);
}
Run Code Online (Sandbox Code Playgroud)

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/);
}
Run Code Online (Sandbox Code Playgroud)

它找到用户,获取他们的推文时间轴,仅过滤前20条推文,应用自定义过滤器,并最终使用回调函数来处理每条推文.

我猜这样设计良好的API应该像查询构建器(想想ORM)一样工作,每个函数调用构建查询(在这种情况下为HTTP URL),直到它遇到循环函数,如每个/ map /等.进行HTTP调用,传入函数成为回调函数.

一个简单的开发途径是使每个AJAX调用同步,但这可能不是最好的解决方案.我有兴趣找出使其异步的方法,并仍然隐藏AJAX的异步性质.

CMS*_*CMS 20

看看几天前由@anywhere的Twitter工程师Dustin Diaz发表的以下文章:

他谈到了一个非常好的技术,它允许你在异步方法上实现一个流畅的接口,基本上是独立于回调链接在一起的方法,使用一个非常简单的Queue实现.


Coo*_*J86 5

我正在开发FutureJS,它最初是基于Crockford的承诺(原始幻灯片).目前的目标是成为JavaScript的异步工具箱并消除链接杂乱.

Futures.chainify(提供者,消费者,背景,参数)

异步方法排队允许您对可能或可能不可用的数据进行链接操作.这就是Twitter的@Anywhere api的工作原理.

您可能需要一个以这种方式远程获取数据的模型:

Contacts.all(params).randomize().limit(10).display();
Contacts.one(id, params).display();
Run Code Online (Sandbox Code Playgroud)

哪个可以像这样实现:

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;
  }
});
Run Code Online (Sandbox Code Playgroud)

要知道的事情:

  • providers - 返回数据的promisables
  • consumers - 使用和/或改变数据的功能
    • 第一个论点必须是 data
    • 当返回promisable时,链中的下一个方法将不会执行,直到履行完成
    • 返回"文字对象"时,链中的下一个方法将使用该对象
    • 当返回undefined(或不返回任何东西)时,链中的下一个方法将使用定义的对象
  • context- apply()d为每个提供者和消费者,从而成为this对象
  • params - 留作将来使用

或者,您可以使用同步回调链接 - 您可能在其他地方看到的链接().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(...)
Run Code Online (Sandbox Code Playgroud)

我命名它sequence而不是chain因为_.js已经有一个名为的方法chain,我也想在我的库中使用_.methodName.

看一看,让我知道你的想法.

FuturesJS将与jQuery,Dojo等一起工作,没有问题.没有依赖关系.它将与Node.js(以及使用env.js时的Rhino)一起使用.

= 8 ^ d

PS对于ORM/MVC修复 - 您可以查看JavaScriptMVCSproutCore.我也正在开发一个名为TriforceJS的自己的解决方案,但我还没有准备好发布任何东西.

PPS示例 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);
Run Code Online (Sandbox Code Playgroud)