【问题标题】:Designing a fluent Javascript interface to abstract away the asynchronous nature of AJAX设计一个流畅的 Javascript 接口来抽象出 AJAX 的异步特性
【发布时间】:2011-02-17 06:58:40
【问题描述】:

我将如何设计一个 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/);
}

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 的异步特性。

【问题讨论】:

  • 我不知道你在问什么。什么是流体接口,异步如何不流体?
  • @mike - 对于异步请求,我们为每个异步请求指定一个回调,这会破坏链接方法的流动性/流畅性。比较上面的两个例子,看看哪一个看起来流畅/流畅。
  • 不是这个问题指定了 jQuery,但是using deferreds in jQuery 1.5 可以很好地抽象出这种调用......还有其他可用的延迟实现。

标签: javascript ajax asynchronous fluent-interface


【解决方案1】:

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

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

【讨论】:

  • 不知道你是怎么找到的,但这看起来正是 OP 需要的。
  • 太棒了.. 很高兴看到 Anywhere API 也在本文背后,现在阅读它 - 谢谢!
  • @Anurag:不客气,希望你喜欢这篇文章。顺便说一句,Dustin 不再在 Yahoo! 工作,他现在在 Twitter 的 @anywhere API 工作:)...
【解决方案2】:

我正在开发 FutureJS,它最初基于 Crockford's promises (original slides)。目前的目标是成为 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 - 返回数据的promisables
  • consumers - 使用和/或更改数据的函数
    • 第一个参数必须是data
    • 当返回一个 Promisable 时,链中的下一个方法在 Promise 完成之前不会执行
    • 返回“文字对象”时,链中的下一个方法将使用该对象
    • 当返回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。

Take a peeklet me know what you think

FuturesJS 可以毫无问题地与 jQuery、Dojo 等一起工作。没有依赖关系。它适用于 Node.js(以及使用 env.js 时的 Rhino)。

=8^D

附:至于 ORM / MVC 修复 - 您可以查看 JavaScriptMVCSproutCore。我也在开发我自己的名为 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);

【讨论】:

  • promisable 基本上像代理吗?
  • promisable 是具有.when().fail().fulfill().smash() 方法的对象。它允许您以异步方式“返回”数据。示例:请参阅我在上面添加的 P.P.S。
【解决方案3】:

我相信,AJAX 同步问题已经被 jQuery 等库抽象出来(即它的 ajax call,它允许您通过 async 属性指定异步或同步操作)。同步模式(如果选择)隐藏了实现的异步特性。

jQuery 也是一个流畅的接口和链接的例子。还有其他库也这样做。节省您重新发明轮子 - 让您立即使用您正在寻找的东西。

如果这可以作为答案,那么您将在这些功能之间获得一些良好的自动浏览器兼容性。从头开始构建这些东西需要很长时间。

我看到 Twitter 的新 Anywhere API 注释 jQuery - 如果您进行一些挖掘,也许一切都已经存在。

【讨论】:

  • 我不希望 AJAX 请求是同步的,但仍然有类似 Object.getDetails().doSomething().display() 的东西,其中 getDetails() 是异步 AJAX 调用。它仍然可以使用 jQuery 实现,但 jQuery 尚未内置此功能。有关更多信息,请参阅@CMS 答案中的文章。 XMLHttpRequest 提供了一个进行同步调用的选项,这并没有隐藏异步的本质,而是完全消除了它。
猜你喜欢
  • 2010-09-30
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 2014-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-14
相关资源
最近更新 更多